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: Plain chat #18284

Merged
merged 27 commits into from
Dec 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8c812f5
plain custom desin
nizzyabi Dec 20, 2024
60bba1f
No display on card
nizzyabi Dec 20, 2024
e9ca446
dynamic plain chat component and hmac hash
nizzyabi Dec 20, 2024
064e8d9
re-route users to plain.com chat instead of intercom chat when going …
nizzyabi Dec 20, 2024
9fbf3f1
provider errors
nizzyabi Dec 20, 2024
9faad04
yarn lock fix
nizzyabi Dec 20, 2024
6138296
plain chat removed unneeded hmac
nizzyabi Dec 20, 2024
ee834d6
fix ts error
nizzyabi Dec 20, 2024
127af7d
error handling improved
nizzyabi Dec 21, 2024
e1b40ea
remove intercome provider from app-dir
nizzyabi Dec 21, 2024
9e2196f
Create getting-started.mdx (#18342)
PeerRich Dec 22, 2024
2abe4fe
Delete help directory (#18343)
calcom-bot Dec 22, 2024
2e43fd8
chore: moved docs/help to /help (#18345)
PeerRich Dec 22, 2024
84a2300
chore: Delete unused guides directory (#18346)
PeerRich Dec 22, 2024
2bba876
feat: booking filters (#18303)
Udit-takkar Dec 23, 2024
468a704
chore: Remove `HeadSeo` components where no longer needed + improve a…
hbjORbj Dec 23, 2024
6581aca
chore: app router - /bookings status page (#18183)
hbjORbj Dec 23, 2024
4810649
cache i18n instances (#18309)
hbjORbj Dec 23, 2024
a3b21ee
feat: virtual queues tab in insights (#18260)
CarinaWolli Dec 23, 2024
8c81e13
feat: update translations via @replexica (#18361)
calcom-bot Dec 23, 2024
291e58b
update OOO e2e tests to remove flakiness (#18367)
vijayraghav-io Dec 24, 2024
e279d02
fix: metadata is overwritten for child managed eventType when updatin…
anikdhabal Dec 24, 2024
08a9a75
chore: added routing support link (#18369)
PeerRich Dec 24, 2024
ab7d481
chore: refactor handling logic for embeds in app router (#18362)
hbjORbj Dec 26, 2024
b02ec98
review changes
nizzyabi Dec 26, 2024
3866ca0
yarn lock
nizzyabi Dec 26, 2024
6068fb4
Merge branch 'main' into plain-chat
emrysal Dec 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ NEXT_PUBLIC_POSTHOG_HOST=
PLAIN_API_KEY=
PLAIN_API_URL=https://api.plain.com/v1
PLAIN_HMAC_SECRET_KEY=
PLAIN_CHAT_ID=
PLAIN_CHAT_HMAC_SECRET_KEY=

# Zendesk Config
NEXT_PUBLIC_ZENDESK_KEY=
Expand Down
29 changes: 5 additions & 24 deletions apps/web/app/api/customer-card/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export async function handler(request: Request) {

// HMAC verification
const incomingSignature = headersList.get("plain-request-signature");
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const expectedSignature = createHmac("sha-256", process.env.PLAIN_HMAC_SECRET_KEY!)
.update(JSON.stringify(requestBody))
.digest("hex");
Expand Down Expand Up @@ -88,18 +89,8 @@ export async function handler(request: Request) {
rowAsideContent: [
{
componentBadge: {
badgeLabel:
customer.emailVerified === undefined
? "Unknown"
: customer.emailVerified
? "Yes"
: "No",
badgeColor:
customer.emailVerified === undefined
? "YELLOW"
: customer.emailVerified
? "GREEN"
: "RED",
badgeLabel: customer.emailVerified ? "Yes" : "No",
badgeColor: customer.emailVerified ? "GREEN" : "RED",
},
},
],
Expand Down Expand Up @@ -195,18 +186,8 @@ export async function handler(request: Request) {
rowAsideContent: [
{
componentBadge: {
badgeLabel:
customer.twoFactorEnabled === undefined
? "Unknown"
: customer.twoFactorEnabled
? "Yes"
: "No",
badgeColor:
customer.twoFactorEnabled === undefined
? "YELLOW"
: customer.twoFactorEnabled
? "GREEN"
: "RED",
badgeLabel: customer.twoFactorEnabled ? "Yes" : "No",
badgeColor: customer.twoFactorEnabled ? "GREEN" : "RED",
},
},
],
Expand Down
49 changes: 49 additions & 0 deletions apps/web/app/api/plain-hash/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { createHmac } from "crypto";
import { NextResponse } from "next/server";
import { z } from "zod";

import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { apiRouteMiddleware } from "@calcom/lib/server/apiRouteMiddleware";

const responseSchema = z.object({
hash: z.string(),
email: z.string().email(),
shortName: z.string(),
appId: z.string(),
fullName: z.string(),
chatAvatarUrl: z.string(),
});

async function handler(request: Request) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const session = await getServerSession({ req: request as any });
if (!session?.user?.email) {
return new Response("Unauthorized - No session email found", { status: 401 });
}

const secret = process.env.PLAIN_CHAT_HMAC_SECRET_KEY;
if (!secret) {
return new Response("Missing Plain Chat secret", { status: 500 });
}

const hmac = createHmac("sha256", secret);
hmac.update(session.user.email.toLowerCase().trim());
const hash = hmac.digest("hex");

const shortName =
(session.user.name?.split(" ")[0] || session.user.email).charAt(0).toUpperCase() +
(session.user.name?.split(" ")[0] || session.user.email).slice(1) || "User";

const response = responseSchema.parse({
hash,
email: session.user.email || "[email protected]",
shortName,
appId: process.env.PLAIN_CHAT_ID,
fullName: session.user.name || "User",
chatAvatarUrl: session.user.avatarUrl || "",
});

return NextResponse.json(response);
}

export const POST = apiRouteMiddleware(handler);
12 changes: 7 additions & 5 deletions apps/web/lib/app-providers-app-dir.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ import CacheProvider from "react-inlinesvg/provider";
import DynamicPostHogProvider from "@calcom/features/ee/event-tracking/lib/posthog/providerDynamic";
import { OrgBrandingProvider } from "@calcom/features/ee/organizations/context/provider";
import DynamicHelpscoutProvider from "@calcom/features/ee/support/lib/helpscout/providerDynamic";
import DynamicIntercomProvider from "@calcom/features/ee/support/lib/intercom/providerDynamic";
import { FeatureProvider } from "@calcom/features/flags/context/provider";
import { useFlags } from "@calcom/features/flags/hooks";
import { MetaProvider } from "@calcom/ui";

import useIsBookingPage from "@lib/hooks/useIsBookingPage";
import PlainChat from "@lib/plain/plainChat";
import type { WithLocaleProps } from "@lib/withLocale";
import type { WithNonceProps } from "@lib/withNonce";

Expand Down Expand Up @@ -265,9 +265,11 @@ function OrgBrandProvider({ children }: { children: React.ReactNode }) {
const AppProviders = (props: PageWrapperProps) => {
// No need to have intercom on public pages - Good for Page Performance
const isBookingPage = useIsBookingPage();

const RemainingProviders = (
<EventCollectionProvider options={{ apiPath: "/api/collect-events" }}>
<SessionProvider>
<PlainChat />
<CustomI18nextProvider i18n={props.i18n}>
<TooltipProvider>
{/* color-scheme makes background:transparent not work which is required by embed. We need to ensure next-theme adds color-scheme to `body` instead of `html`(https://github.com/pacocoursey/next-themes/blob/main/src/index.tsx#L74). Once that's done we can enable color-scheme support */}
Expand Down Expand Up @@ -301,11 +303,11 @@ const AppProviders = (props: PageWrapperProps) => {
}

return (
<DynamicHelpscoutProvider>
<DynamicIntercomProvider>
<>
<DynamicHelpscoutProvider>
<DynamicPostHogProvider>{Hydrated}</DynamicPostHogProvider>
</DynamicIntercomProvider>
</DynamicHelpscoutProvider>
</DynamicHelpscoutProvider>
</>
);
};

Expand Down
13 changes: 6 additions & 7 deletions apps/web/lib/app-providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ import CacheProvider from "react-inlinesvg/provider";
import DynamicPostHogProvider from "@calcom/features/ee/event-tracking/lib/posthog/providerDynamic";
import { OrgBrandingProvider } from "@calcom/features/ee/organizations/context/provider";
import DynamicHelpscoutProvider from "@calcom/features/ee/support/lib/helpscout/providerDynamic";
import DynamicIntercomProvider from "@calcom/features/ee/support/lib/intercom/providerDynamic";
import { FeatureProvider } from "@calcom/features/flags/context/provider";
import { useFlags } from "@calcom/features/flags/hooks";
import { MetaProvider } from "@calcom/ui";

import useIsBookingPage from "@lib/hooks/useIsBookingPage";
import PlainChat from "@lib/plain/plainChat";
import type { WithLocaleProps } from "@lib/withLocale";
import type { WithNonceProps } from "@lib/withNonce";

Expand Down Expand Up @@ -282,7 +282,6 @@ function OrgBrandProvider({ children }: { children: React.ReactNode }) {
}

const AppProviders = (props: AppPropsWithChildren) => {
// No need to have intercom on public pages - Good for Page Performance
const isBookingPage = useIsBookingPage();
const { pageProps, ...rest } = props;

Expand All @@ -298,9 +297,9 @@ const AppProviders = (props: AppPropsWithChildren) => {
const RemainingProviders = (
<EventCollectionProvider options={{ apiPath: "/api/collect-events" }}>
<SessionProvider session={pageProps.session ?? undefined}>
<PlainChat />
<CustomI18nextProvider {...propsWithoutNonce}>
<TooltipProvider>
{/* color-scheme makes background:transparent not work which is required by embed. We need to ensure next-theme adds color-scheme to `body` instead of `html`(https://github.com/pacocoursey/next-themes/blob/main/src/index.tsx#L74). Once that's done we can enable color-scheme support */}
<CalcomThemeProvider
themeBasis={props.pageProps.themeBasis}
nonce={props.pageProps.nonce}
Expand All @@ -327,14 +326,14 @@ const AppProviders = (props: AppPropsWithChildren) => {
}

return (
<DynamicHelpscoutProvider>
<DynamicIntercomProvider>
<>
<DynamicHelpscoutProvider>
<DynamicPostHogProvider>
<PostHogPageView />
{RemainingProviders}
</DynamicPostHogProvider>
</DynamicIntercomProvider>
</DynamicHelpscoutProvider>
</DynamicHelpscoutProvider>
</>
);
};

Expand Down
Loading
Loading