Skip to content

Commit

Permalink
feat: Plain chat (#18284)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* 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 <[email protected]>
Co-authored-by: sean-brydon <[email protected]>
Co-authored-by: sean-brydon <[email protected]>
Co-authored-by: Peer Richelsen <[email protected]>
Co-authored-by: Omar López <[email protected]>

* feat: update translations via @replexica (#18361)

Co-authored-by: Replexica <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>
Co-authored-by: Calcom Bot <[email protected]>
Co-authored-by: Udit Takkar <[email protected]>
Co-authored-by: Syed Ali Shahbaz <[email protected]>
Co-authored-by: Benny Joo <[email protected]>
Co-authored-by: Carina Wollendorfer <[email protected]>
Co-authored-by: CarinaWolli <[email protected]>
Co-authored-by: sean-brydon <[email protected]>
Co-authored-by: sean-brydon <[email protected]>
Co-authored-by: Omar López <[email protected]>
Co-authored-by: Replexica <[email protected]>
Co-authored-by: Vijay <[email protected]>
Co-authored-by: Anik Dhabal Babu <[email protected]>
Co-authored-by: Joe Au-Yeung <[email protected]>
Co-authored-by: Alex van Andel <[email protected]>
  • Loading branch information
16 people authored Dec 26, 2024
1 parent 3994929 commit 225313e
Show file tree
Hide file tree
Showing 12 changed files with 7,767 additions and 210 deletions.
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

0 comments on commit 225313e

Please sign in to comment.