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
|
||||
NEXT_PUBLIC_HELPSCOUT_KEY=
|
||||
|
||||
# Fresh Chat Config
|
||||
NEXT_PUBLIC_FRESHCHAT_TOKEN=
|
||||
NEXT_PUBLIC_FRESHCHAT_HOST=
|
||||
|
||||
# Inbox to send user feedback
|
||||
SEND_FEEDBACK_EMAIL=
|
||||
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
import FreshChatMenuItem from "../lib/freshchat/FreshChatMenuItem";
|
||||
import HelpscoutMenuItem from "../lib/helpscout/HelpscoutMenuItem";
|
||||
import IntercomMenuItem from "../lib/intercom/IntercomMenuItem";
|
||||
import ZendeskMenuItem from "../lib/zendesk/ZendeskMenuItem";
|
||||
|
||||
export default function HelpMenuItem() {
|
||||
interface ContactMenuItem {
|
||||
onHelpItemSelect: () => void;
|
||||
}
|
||||
|
||||
export default function ContactMenuItem(props: ContactMenuItem) {
|
||||
const { onHelpItemSelect } = props;
|
||||
return (
|
||||
<>
|
||||
<IntercomMenuItem />
|
||||
<ZendeskMenuItem />
|
||||
<HelpscoutMenuItem />
|
||||
<IntercomMenuItem onHelpItemSelect={onHelpItemSelect} />
|
||||
<ZendeskMenuItem onHelpItemSelect={onHelpItemSelect} />
|
||||
<HelpscoutMenuItem onHelpItemSelect={onHelpItemSelect} />
|
||||
<FreshChatMenuItem onHelpItemSelect={onHelpItemSelect} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import { trpc } from "@calcom/trpc/react";
|
|||
import { Button, showToast } from "@calcom/ui";
|
||||
import { FiExternalLink, FiAlertTriangle } from "@calcom/ui/components/icon";
|
||||
|
||||
import { useFreshChat } from "../lib/freshchat/FreshChatProvider";
|
||||
import { isFreshChatEnabled } from "../lib/freshchat/FreshChatScript";
|
||||
import ContactMenuItem from "./ContactMenuItem";
|
||||
|
||||
interface HelpMenuItemProps {
|
||||
|
@ -21,6 +23,8 @@ export default function HelpMenuItem({ onHelpItemSelect }: HelpMenuItemProps) {
|
|||
const [, loadChat] = useChat();
|
||||
const { t } = useLocale();
|
||||
|
||||
const { setActive: setFreshChat } = useFreshChat();
|
||||
|
||||
const mutation = trpc.viewer.submitFeedback.useMutation({
|
||||
onSuccess: () => {
|
||||
setDisableSubmit(true);
|
||||
|
@ -43,7 +47,6 @@ export default function HelpMenuItem({ onHelpItemSelect }: HelpMenuItemProps) {
|
|||
<div className="w-full py-5">
|
||||
<p className="mb-1 px-5 text-gray-500">{t("resources").toUpperCase()}</p>
|
||||
<a
|
||||
onClick={() => onHelpItemSelect()}
|
||||
href="https://docs.cal.com/"
|
||||
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"
|
||||
|
@ -57,7 +60,6 @@ export default function HelpMenuItem({ onHelpItemSelect }: HelpMenuItemProps) {
|
|||
/>
|
||||
</a>
|
||||
<a
|
||||
onClick={() => onHelpItemSelect()}
|
||||
href="https://developer.cal.com/"
|
||||
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"
|
||||
|
@ -70,8 +72,8 @@ export default function HelpMenuItem({ onHelpItemSelect }: HelpMenuItemProps) {
|
|||
)}
|
||||
/>
|
||||
</a>
|
||||
<div onClick={() => onHelpItemSelect()}>
|
||||
<ContactMenuItem />
|
||||
<div>
|
||||
<ContactMenuItem onHelpItemSelect={onHelpItemSelect} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -202,7 +204,11 @@ export default function HelpMenuItem({ onHelpItemSelect }: HelpMenuItemProps) {
|
|||
className="font-medium underline hover:text-gray-700"
|
||||
onClick={() => {
|
||||
setActive(true);
|
||||
if (isFreshChatEnabled) {
|
||||
setFreshChat(true);
|
||||
} else {
|
||||
loadChat({ open: true });
|
||||
}
|
||||
onHelpItemSelect();
|
||||
}}>
|
||||
{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";
|
||||
|
||||
export default function HelpscoutMenuItem() {
|
||||
interface HelpscoutMenuItemProps {
|
||||
onHelpItemSelect: () => void;
|
||||
}
|
||||
|
||||
export default function HelpscoutMenuItem(props: HelpscoutMenuItemProps) {
|
||||
const { onHelpItemSelect } = props;
|
||||
const { t } = useLocale();
|
||||
const [active, setActive] = useState(false);
|
||||
|
||||
|
@ -12,10 +17,12 @@ export default function HelpscoutMenuItem() {
|
|||
function handleClick() {
|
||||
setActive(true);
|
||||
loadChat({ open: true });
|
||||
onHelpItemSelect();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||
if (!process.env.NEXT_PUBLIC_HELPSCOUT_KEY) return null;
|
||||
else
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
|
|
|
@ -2,16 +2,23 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|||
|
||||
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 { boot, show } = useIntercom();
|
||||
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||
if (!process.env.NEXT_PUBLIC_INTERCOM_APP_ID) return null;
|
||||
else
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => {
|
||||
boot();
|
||||
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">
|
||||
{t("contact_support")}
|
||||
|
|
|
@ -3,18 +3,27 @@ import { useState } from "react";
|
|||
|
||||
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;
|
||||
|
||||
export default function ZendeskMenuItem() {
|
||||
interface ZendeskMenuItemProps {
|
||||
onHelpItemSelect: () => void;
|
||||
}
|
||||
|
||||
export default function ZendeskMenuItem(props: ZendeskMenuItemProps) {
|
||||
const { onHelpItemSelect } = props;
|
||||
const [active, setActive] = useState(false);
|
||||
const { t } = useLocale();
|
||||
|
||||
if (!process.env.NEXT_PUBLIC_ZENDESK_KEY) return null;
|
||||
else
|
||||
if (!ZENDESK_KEY) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<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">
|
||||
{t("contact_support")}
|
||||
</button>
|
||||
|
|
|
@ -63,6 +63,7 @@ import {
|
|||
FiArrowLeft,
|
||||
} from "@calcom/ui/components/icon";
|
||||
|
||||
import FreshChatProvider from "../ee/support/lib/freshchat/FreshChatProvider";
|
||||
import { TeamInviteBadge } from "./TeamInviteBadge";
|
||||
|
||||
/* TODO: Migate this */
|
||||
|
@ -316,6 +317,7 @@ function UserDropdown({ small }: { small?: boolean }) {
|
|||
</div>
|
||||
|
||||
<DropdownMenuPortal>
|
||||
<FreshChatProvider>
|
||||
<DropdownMenuContent
|
||||
onInteractOutside={() => {
|
||||
setMenuOpen(false);
|
||||
|
@ -399,7 +401,11 @@ function UserDropdown({ small }: { small?: boolean }) {
|
|||
</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
<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")}
|
||||
</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
|
@ -416,6 +422,7 @@ function UserDropdown({ small }: { small?: boolean }) {
|
|||
</>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</FreshChatProvider>
|
||||
</DropdownMenuPortal>
|
||||
</Dropdown>
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue