Merge branch 'main' into fix/cal-video-past

This commit is contained in:
Keith Williams 2024-01-02 11:59:40 -05:00 committed by GitHub
commit d73d26f033
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 252 additions and 147 deletions

View File

@ -133,6 +133,11 @@ NEXT_PUBLIC_SENDGRID_SENDER_NAME=
# Used for capturing exceptions and logging messages
NEXT_PUBLIC_SENTRY_DSN=
# Formbricks Experience Management Integration
FORMBRICKS_HOST_URL=https://app.formbricks.com
FORMBRICKS_ENVIRONMENT_ID=
FORMBRICKS_FEEDBACK_SURVEY_ID=
# Twilio
# Used to send SMS reminders in workflows
TWILIO_SID=

View File

@ -1,74 +0,0 @@
name: "Apply issue labels to PR"
on:
pull_request_target:
types:
- opened
jobs:
label_on_pr:
runs-on: ubuntu-latest
permissions:
contents: none
issues: read
pull-requests: write
steps:
- name: Apply labels from linked issue to PR
uses: actions/github-script@v5
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
async function getLinkedIssues(owner, repo, prNumber) {
const query = `query GetLinkedIssues($owner: String!, $repo: String!, $prNumber: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
closingIssuesReferences(first: 10) {
nodes {
number
labels(first: 10) {
nodes {
name
}
}
}
}
}
}
}`;
const variables = {
owner: owner,
repo: repo,
prNumber: prNumber,
};
const result = await github.graphql(query, variables);
return result.repository.pullRequest.closingIssuesReferences.nodes;
}
const pr = context.payload.pull_request;
const linkedIssues = await getLinkedIssues(
context.repo.owner,
context.repo.repo,
pr.number
);
const labelsToAdd = new Set();
for (const issue of linkedIssues) {
if (issue.labels && issue.labels.nodes) {
for (const label of issue.labels.nodes) {
labelsToAdd.add(label.name);
}
}
}
if (labelsToAdd.size) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
labels: Array.from(labelsToAdd),
});
}

View File

@ -16,3 +16,81 @@ jobs:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
# https://github.com/actions/labeler/issues/442#issuecomment-1297359481
sync-labels: ""
team-labels:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: equitybee/team-label-action@main
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
organization-name: calcom
ignore-labels: "app-store, ai, authentication, automated-testing, platform, billing, bookings, caldav, calendar-apps, ci, console, crm-apps, docs, documentation, emails, embeds, event-types, i18n, impersonation, manual-testing, ui, performance, ops-stack, organizations, public-api, routing-forms, seats, teams, webhooks, workflows, zapier"
apply-labels-from-issue:
runs-on: ubuntu-latest
permissions:
contents: none
issues: read
pull-requests: write
steps:
- name: Apply labels from linked issue to PR
uses: actions/github-script@v5
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
async function getLinkedIssues(owner, repo, prNumber) {
const query = `query GetLinkedIssues($owner: String!, $repo: String!, $prNumber: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
closingIssuesReferences(first: 10) {
nodes {
number
labels(first: 10) {
nodes {
name
}
}
}
}
}
}
}`;
const variables = {
owner: owner,
repo: repo,
prNumber: prNumber,
};
const result = await github.graphql(query, variables);
return result.repository.pullRequest.closingIssuesReferences.nodes;
}
const pr = context.payload.pull_request;
const linkedIssues = await getLinkedIssues(
context.repo.owner,
context.repo.repo,
pr.number
);
const labelsToAdd = new Set();
for (const issue of linkedIssues) {
if (issue.labels && issue.labels.nodes) {
for (const label of issue.labels.nodes) {
labelsToAdd.add(label.name);
}
}
}
if (labelsToAdd.size) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
labels: Array.from(labelsToAdd),
});
}

View File

@ -1,16 +0,0 @@
name: Assign PR team labels
on:
pull_request:
branches:
- main
jobs:
team-labels:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: equitybee/team-label-action@main
with:
repo-token: ${{ secrets.GH_ACCESS_TOKEN }}
organization-name: calcom
ignore-labels: "app-store, ai, authentication, automated-testing, platform, billing, bookings, caldav, calendar-apps, ci, console, crm-apps, docs, documentation, emails, embeds, event-types, i18n, impersonation, manual-testing, ui, performance, ops-stack, organizations, public-api, routing-forms, seats, teams, webhooks, workflows, zapier"

View File

@ -48,17 +48,21 @@ Here is the full architecture:
### Email Router
To expose the AI app, run `ngrok http 3005` (or the AI app's port number) in a new terminal. You may need to install [nGrok](https://ngrok.com/).
To expose the AI app, you can use either [Tunnelmole](https://github.com/robbie-cahill/tunnelmole-client), an open source tunnelling tool; or [nGrok](https://ngrok.com/), a popular closed source tunnelling tool.
For Tunnelmole, run `tmole 3005` (or the AI app's port number) in a new terminal. Please replace `3005` with the port number if it is different. In the output, you'll see two URLs, one http and a https (we recommend using the https url for privacy and security). To install Tunnelmole, use `curl -O https://install.tunnelmole.com/8dPBw/install && sudo bash install`. (On Windows, download [tmole.exe](https://tunnelmole.com/downloads/tmole.exe))
For nGrok, run `ngrok http 3005` (or the AI app's port number) in a new terminal. You may need to install nGrok first.
To forward incoming emails to the serverless function at `/agent`, we use [SendGrid's Inbound Parse](https://docs.sendgrid.com/for-developers/parsing-email/setting-up-the-inbound-parse-webhook).
1. Ensure you have a [SendGrid account](https://signup.sendgrid.com/)
2. Ensure you have an authenticated domain. Go to Settings > Sender Authentication > Authenticate. For DNS host, select `I'm not sure`. Click Next and add your domain, eg. `example.com`. Choose Manual Setup. You'll be given three CNAME records to add to your DNS settings, eg. in [Vercel Domains](https://vercel.com/dashboard/domains). After adding those records, click Verify. To troubleshoot, see the [full instructions](https://docs.sendgrid.com/ui/account-and-settings/how-to-set-up-domain-authentication).
3. Authorize your domain for email with MX records: one with name `[your domain].com` and value `mx.sendgrid.net.`, and another with name `bounces.[your domain].com` and value `feedback-smtp.us-east-1.amazonses.com`, both with priority `10` if prompted.
3. Authorize your domain for email with MX records: one with name `[your domain].com` and value `mx.sendgrid.net.`, and another with name `bounces.[your domain].com` and value `feedback-smtp.us-east-1.amazonses.com`. Set the priority to `10` if prompted.
4. Go to Settings > [Inbound Parse](https://app.sendgrid.com/settings/parse) > Add Host & URL. Choose your authenticated domain.
5. In the Destination URL field, use the nGrok URL from above along with the path, `/api/receive`, and one param, `parseKey`, which lives in [this app's .env](/apps/ai/.env.example) under `PARSE_KEY`. The full URL should look like `https://abc.ngrok.io/api/receive?parseKey=ABC-123`.
5. In the Destination URL field, use the Tunnelmole or ngrok URL from above along with the path, `/api/receive`, and one param, `parseKey`, which lives in [this app's .env](/apps/ai/.env.example) under `PARSE_KEY`. The full URL should look like `https://abc.tunnelmole.net/api/receive?parseKey=ABC-123` or `https://abc.ngrok.io/api/receive?parseKey=ABC-123`.
6. Activate "POST the raw, full MIME message".
7. Send an email to `[anyUsername]@example.com`. You should see a ping on the nGrok listener and server.
7. Send an email to `[anyUsername]@example.com`. You should see a ping on the Tunnelmole or ngrok listener and server.
8. Adjust the logic in [receive/route.ts](/apps/ai/src/app/api/receive/route.ts), save to hot-reload, and send another email to test the behaviour.
Please feel free to improve any part of this architecture!

View File

@ -39,7 +39,7 @@
"@calcom/tsconfig": "*",
"@calcom/ui": "*",
"@daily-co/daily-js": "^0.37.0",
"@formkit/auto-animate": "^1.0.0-beta.5",
"@formkit/auto-animate": "^0.8.1",
"@glidejs/glide": "^3.5.2",
"@hookform/error-message": "^2.0.0",
"@hookform/resolvers": "^2.9.7",

View File

@ -15,7 +15,7 @@ function VerifyEmailPage() {
const { data } = useEmailVerifyCheck();
const { data: session } = useSession();
const router = useRouter();
const { t } = useLocale();
const { t, isLocaleReady } = useLocale();
const mutation = trpc.viewer.auth.resendVerifyEmail.useMutation();
useEffect(() => {
@ -24,7 +24,9 @@ function VerifyEmailPage() {
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data?.isVerified]);
if (!isLocaleReady) {
return null;
}
return (
<div className="h-[100vh] w-full ">
<div className="flex h-full w-full flex-col items-center justify-center">

View File

@ -522,6 +522,32 @@ const EventTypePage = (props: EventTypeSetupProps) => {
const [slugExistsChildrenDialogOpen, setSlugExistsChildrenDialogOpen] = useState<ChildrenEventType[]>([]);
const slug = formMethods.watch("slug") ?? eventType.slug;
// Optional prerender all tabs after 300 ms on mount
useEffect(() => {
const timeout = setTimeout(() => {
const Components = [
EventSetupTab,
EventAvailabilityTab,
EventTeamTab,
EventLimitsTab,
EventAdvancedTab,
EventInstantTab,
EventRecurringTab,
EventAppsTab,
EventWorkflowsTab,
EventWebhooksTab,
];
Components.forEach((C) => {
// @ts-expect-error Property 'render' does not exist on type 'ComponentClass
C.render.preload();
});
}, 300);
return () => {
clearTimeout(timeout);
};
}, []);
return (
<>
<EventTypeSingleLayout

View File

@ -162,10 +162,6 @@ html.todesktop .desktop-hidden {
display: none;
}
html.todesktop header {
margin-top: -14px;
}
html.todesktop header nav {
margin-top: 8px;
}
@ -184,28 +180,38 @@ html.todesktop-platform-darwin.dark main.bg-default {
}
html.todesktop-platform-darwin.light main.bg-default {
background: rgba(255, 255, 255, 0.8) !important;
background: rgba(255, 255, 255, 0.6) !important;
}
/*
html.todesktop aside a {
height: 28px;
padding: 0px 8px;
font-size: 12px;
color: #383438 !important
html.todesktop.light nav a[aria-current="page"]{
background: #CFD0D0 !important;
}
html.todesktop nav a:hover{
background-color: inherit !important
html.todesktop.dark nav a[aria-current="page"]{
background: #3D3D3D !important;
}
html.todesktop nav a[aria-current="page"]{
background: rgba(0, 0, 0, 0.1) !important;
html.todesktop aside header {
margin-top: -12px;
flex-direction: column-reverse;
}
html.todesktop aside header > div {
width: 100%;
}
html.todesktop.dark nav a:hover{
background: inherit;
}
html.todesktop.light nav a:hover{
background: none;
}
html.todesktop nav a svg{
color: #0272F7 !important
} */
color: #229CFF !important
}
/*
Adds Utility to hide scrollbar to tailwind

View File

@ -8,7 +8,7 @@ import { expect, vi } from "vitest";
import "vitest-fetch-mock";
import dayjs from "@calcom/dayjs";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { CAL_URL } from "@calcom/lib/constants";
import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
import { BookingStatus } from "@calcom/prisma/enums";
@ -382,7 +382,7 @@ export function expectSuccessfulBookingCreationEmails({
bookingTimeRange?: { start: Date; end: Date };
booking: { uid: string; urlOrigin?: string };
}) {
const bookingUrlOrigin = booking.urlOrigin || WEBAPP_URL;
const bookingUrlOrigin = booking.urlOrigin || CAL_URL;
expect(emails).toHaveEmail(
{
titleTag: "confirmed_event_type_subject",
@ -742,7 +742,7 @@ export function expectBookingRequestRescheduledEmails({
booking: { uid: string; urlOrigin?: string };
bookNewTimePath: string;
}) {
const bookingUrlOrigin = booking.urlOrigin || WEBAPP_URL;
const bookingUrlOrigin = booking.urlOrigin || CAL_URL;
expect(emails).toHaveEmail(
{

View File

@ -338,12 +338,11 @@ export const BookEventFormChild = ({
// Ensures that duration is an allowed value, if not it defaults to the
// default eventQuery duration.
const validDuration =
duration &&
eventQuery.data.metadata?.multipleDuration &&
eventQuery.data.metadata?.multipleDuration.includes(duration)
? duration
: eventQuery.data.length;
const validDuration = eventQuery.data.isDynamic
? duration || eventQuery.data.length
: duration && eventQuery.data.metadata?.multipleDuration?.includes(duration)
? duration
: eventQuery.data.length;
const bookingInput = {
values,

View File

@ -12,7 +12,7 @@ import type { NextApiRequest, NextApiResponse } from "next";
import { describe, expect } from "vitest";
import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { CAL_URL, WEBAPP_URL } from "@calcom/lib/constants";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { resetTestEmails } from "@calcom/lib/testEmails";
import { BookingStatus } from "@calcom/prisma/enums";
@ -218,7 +218,7 @@ describe("handleNewBooking", () => {
expectSuccessfulBookingCreationEmails({
booking: {
uid: createdBooking.uid!,
urlOrigin: org ? org.urlOrigin : WEBAPP_URL,
urlOrigin: org ? org.urlOrigin : CAL_URL,
},
booker,
organizer,

View File

@ -1,7 +1,7 @@
import { v4 as uuidv4 } from "uuid";
import { describe, expect } from "vitest";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { WEBAPP_URL, CAL_URL } from "@calcom/lib/constants";
import { ErrorCode } from "@calcom/lib/errorCodes";
import logger from "@calcom/lib/logger";
import { BookingStatus } from "@calcom/prisma/enums";
@ -209,7 +209,7 @@ describe("handleNewBooking", () => {
booking: {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
uid: createdBookings[0].uid!,
urlOrigin: WEBAPP_URL,
urlOrigin: CAL_URL,
},
organizer,
emails,
@ -555,7 +555,7 @@ describe("handleNewBooking", () => {
booking: {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
uid: createdBookings[0].uid!,
urlOrigin: WEBAPP_URL,
urlOrigin: CAL_URL,
},
organizer,
emails,
@ -769,7 +769,7 @@ describe("handleNewBooking", () => {
booking: {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
uid: createdBookings[0].uid!,
urlOrigin: WEBAPP_URL,
urlOrigin: CAL_URL,
},
booker,
organizer,

View File

@ -3,7 +3,7 @@ import type { NextApiRequest, NextApiResponse } from "next";
import { describe, expect } from "vitest";
import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { CAL_URL, WEBAPP_URL } from "@calcom/lib/constants";
import { ErrorCode } from "@calcom/lib/errorCodes";
import { SchedulingType } from "@calcom/prisma/enums";
import { BookingStatus } from "@calcom/prisma/enums";
@ -1279,7 +1279,7 @@ describe("handleNewBooking", () => {
booking: {
uid: createdBooking.uid!,
// All booking links are of WEBAPP_URL and not of the org because the team isn't part of the org
urlOrigin: WEBAPP_URL,
urlOrigin: CAL_URL,
},
booker,
organizer,

View File

@ -37,7 +37,35 @@ Browsers do not allow camera/mic access on any non-HTTPS hosts except for localh
For eg:- Use `http://localhost:3000/video/nAjnkjejuzis99NhN72rGt` instead of `http://app.cal.local:3000/video/nAjnkjejuzis99NhN72rGt`.
You can also use `ngrok` or you can generate SSL certificate for your local domain using `mkcert`.
To get an HTTPS URL for localhost, you can use a tunneling tool such as `ngrok` or [Tunnelmole](https://github.com/robbie-cahill/tunnelmole-client) . Alternatively, you can generate an SSL certificate for your local domain using `mkcert`. Turn off any SSL certificate validation in your HTTPS client (be sure to do this for local only, otherwise its a security risk).
#### Tunnelmole - Open Source Tunnelling Tool:
To install Tunnelmole, execute the command:
```
curl -O https://install.tunnelmole.com/8dPBw/install && sudo bash install
```
After a successful installation, you can run Tunnelmole using the following command, replacing `8000` with your actual port number if it is different:
```
tmole 8000
```
In the output, you'll see two URLs, one HTTP and an HTTPS URL. For privacy and security reasons, it is recommended to use the HTTPS URL.
View the Tunnelmole [README](https://github.com/robbie-cahill/tunnelmole-client) for additional information and other installation methods such as `npm` or building your own binaries from source.
#### ngrok - Closed Source Tunnelling Tool:
ngrok is a popular closed source tunneling tool. You can run ngrok using the same port, using the format `ngrok http <port>` replacing `<port>` with your actual port number. For example:
```
ngrok http 8000
```
This will generate a public URL that you can use to access your localhost server.
## DNS setup

View File

@ -151,7 +151,7 @@ const ProfileView = () => {
{isAdmin ? (
<TeamProfileForm team={team} />
) : (
<div className="flex">
<div className="border-subtle flex rounded-b-xl border border-t-0 px-4 py-8 sm:px-6">
<div className="flex-grow">
<div>
<Label className="text-emphasis">{t("team_name")}</Label>
@ -167,7 +167,7 @@ const ProfileView = () => {
</>
)}
</div>
<div className="">
<div>
<Link href={permalink} passHref={true} target="_blank">
<LinkIconButton Icon={ExternalLink}>{t("preview")}</LinkIconButton>
</Link>

View File

@ -103,7 +103,7 @@ const tabs: VerticalTabItemProps[] = [
},
{
name: "teams",
href: "/settings/teams",
href: "/teams",
icon: Users,
children: [],
},

View File

@ -105,7 +105,7 @@ const tabs: VerticalTabItemProps[] = [
},
{
name: "teams",
href: "/settings/teams",
href: "/teams",
icon: Users,
children: [],
},

View File

@ -421,7 +421,7 @@ function UserDropdown({ small }: UserDropdownProps) {
<DropdownMenuTrigger asChild onClick={() => setMenuOpen((menuOpen) => !menuOpen)}>
<button
className={classNames(
"hover:bg-emphasis group mx-0 flex cursor-pointer appearance-none items-center rounded-full text-left outline-none transition focus:outline-none focus:ring-0 md:rounded-none lg:rounded",
"hover:bg-emphasis group mx-0 flex w-full cursor-pointer appearance-none items-center rounded-full text-left outline-none transition focus:outline-none focus:ring-0 md:rounded-none lg:rounded",
small ? "p-2" : "px-2 py-1.5"
)}>
<span
@ -913,7 +913,7 @@ function SideBar({ bannersHeight, user }: SideBarProps) {
</span>
</div>
)}
<div className="flex space-x-0.5 rtl:space-x-reverse">
<div className="flex justify-end space-x-0.5 rtl:space-x-reverse">
<button
color="minimal"
onClick={() => window.history.back()}
@ -935,8 +935,6 @@ function SideBar({ bannersHeight, user }: SideBarProps) {
</div>
</header>
<hr className="desktop-only border-subtle absolute -left-3 -right-3 mt-4 block w-full" />
{/* logo icon for tablet */}
<Link href="/event-types" className="text-center md:inline lg:hidden">
<Logo small icon />

View File

@ -78,7 +78,7 @@ const WebhooksView = () => {
<></>
)
}
borderInShellHeader={data && data.profiles.length === 1}
borderInShellHeader={(data && data.profiles.length === 1) || !data?.webhookGroups?.length}
/>
<div>
<WebhooksList webhooksByViewer={data} />

View File

@ -0,0 +1,42 @@
import { FormbricksAPI } from "@formbricks/api";
import type { Feedback } from "@calcom/emails/templates/feedback-email";
enum Rating {
"Extremely unsatisfied" = 1,
"Unsatisfied" = 2,
"Satisfied" = 3,
"Extremely satisfied" = 4,
}
export const sendFeedbackFormbricks = async (userId: number, feedback: Feedback) => {
if (!process.env.FORMBRICKS_HOST_URL || !process.env.FORMBRICKS_ENVIRONMENT_ID)
throw new Error("Missing FORMBRICKS_HOST_URL or FORMBRICKS_ENVIRONMENT_ID env variable");
const api = new FormbricksAPI({
apiHost: process.env.FORMBRICKS_HOST_URL,
environmentId: process.env.FORMBRICKS_ENVIRONMENT_ID,
});
if (process.env.FORMBRICKS_FEEDBACK_SURVEY_ID) {
const formbricksUserId = userId.toString();
const ratingValue = Object.keys(Rating).includes(feedback.rating)
? Rating[feedback.rating as keyof typeof Rating]
: undefined;
if (ratingValue === undefined) throw new Error("Invalid rating value");
await api.client.response.create({
surveyId: process.env.FORMBRICKS_FEEDBACK_SURVEY_ID,
userId: formbricksUserId,
finished: true,
data: {
"formbricks-share-comments-question": feedback.comment,
"formbricks-rating-question": ratingValue,
},
});
await api.client.people.update(formbricksUserId, {
attributes: {
email: feedback.email,
username: feedback.username,
},
});
}
};

View File

@ -1,12 +1,12 @@
import { WEBAPP_URL } from "../constants";
import { CAL_URL } from "../constants";
import { getBrand } from "../server/getBrand";
export const getBookerBaseUrl = async (user: { organizationId: number | null }) => {
const orgBrand = await getBrand(user.organizationId);
return orgBrand?.fullDomain ?? WEBAPP_URL;
return orgBrand?.fullDomain ?? CAL_URL;
};
export const getTeamBookerUrl = async (team: { organizationId: number | null }) => {
const orgBrand = await getBrand(team.organizationId);
return orgBrand?.fullDomain ?? WEBAPP_URL;
return orgBrand?.fullDomain ?? CAL_URL;
};

View File

@ -15,7 +15,7 @@ import { customInputSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-u
import { TRPCError } from "@trpc/server";
import { WEBAPP_URL } from "./constants";
import { CAL_URL } from "./constants";
import { getBookerBaseUrl } from "./getBookerUrl/server";
interface getEventTypeByIdProps {
@ -271,7 +271,7 @@ export default async function getEventTypeById({
? await getBookerBaseUrl({ organizationId: restEventType.team.parentId })
: restEventType.owner
? await getBookerBaseUrl(restEventType.owner)
: WEBAPP_URL,
: CAL_URL,
children: restEventType.children.flatMap((ch) =>
ch.owner !== null
? {

View File

@ -14,6 +14,7 @@
"dependencies": {
"@calcom/config": "*",
"@calcom/dayjs": "*",
"@formbricks/api": "^1.1.0",
"@sendgrid/client": "^7.7.0",
"@vercel/og": "^0.5.0",
"bcryptjs": "^2.4.3",

View File

@ -1,5 +1,6 @@
import dayjs from "@calcom/dayjs";
import { sendFeedbackEmail } from "@calcom/emails";
import { sendFeedbackFormbricks } from "@calcom/lib/formbricks";
import { prisma } from "@calcom/prisma";
import type { TrpcSessionUser } from "@calcom/trpc/server/trpc";
@ -30,6 +31,8 @@ export const submitFeedbackHandler = async ({ ctx, input }: SubmitFeedbackOption
comment: comment,
},
});
if (process.env.FORMBRICKS_HOST_URL && process.env.FORMBRICKS_ENVIRONMENT_ID)
sendFeedbackFormbricks(ctx.user.id, feedback);
if (process.env.SEND_FEEDBACK_EMAIL && comment) sendFeedbackEmail(feedback);
};

View File

@ -24,7 +24,7 @@
"dependencies": {
"@calcom/lib": "*",
"@calcom/trpc": "*",
"@formkit/auto-animate": "^1.0.0-beta.5",
"@formkit/auto-animate": "^0.8.1",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-popover": "^1.0.2",

View File

@ -256,6 +256,9 @@
"EMAIL_SERVER_USER",
"EMAIL_SERVER",
"EXCHANGE_DEFAULT_EWS_URL",
"FORMBRICKS_HOST_URL",
"FORMBRICKS_ENVIRONMENT_ID",
"FORMBRICKS_FEEDBACK_SURVEY_ID",
"GIPHY_API_KEY",
"GITHUB_API_REPO_TOKEN",
"GOOGLE_API_CREDENTIALS",