diff --git a/apps/api/v2/src/modules/slots/slots-2024-04-15/controllers/slots.controller.e2e-spec.ts b/apps/api/v2/src/modules/slots/slots-2024-04-15/controllers/slots.controller.e2e-spec.ts
index f452d47cae..3a7283f982 100644
--- a/apps/api/v2/src/modules/slots/slots-2024-04-15/controllers/slots.controller.e2e-spec.ts
+++ b/apps/api/v2/src/modules/slots/slots-2024-04-15/controllers/slots.controller.e2e-spec.ts
@@ -468,7 +468,7 @@ describe("Slots 2024-04-15 Endpoints", () => {
it("should do a booking and slot should not be available at that time", async () => {
const startTime = "2050-09-05T11:00:00.000Z";
const booking = await bookingsRepositoryFixture.create({
- uid: `booking-uid-${randomString()}`,
+ uid: `booking-uid-${eventTypeId}`,
title: "booking title",
startTime,
endTime: "2050-09-05T12:00:00.000Z",
@@ -515,7 +515,7 @@ describe("Slots 2024-04-15 Endpoints", () => {
it("should do a booking for seated event and slot should show attendees count and bookingUid", async () => {
const startTime = "2050-09-05T11:00:00.000Z";
const booking = await bookingsRepositoryFixture.create({
- uid: `booking-uid-${randomString()}`,
+ uid: `booking-uid-seated-${seatedEventTypeId}-${randomString()}`,
title: "booking title",
startTime,
endTime: "2050-09-05T12:00:00.000Z",
@@ -549,7 +549,7 @@ describe("Slots 2024-04-15 Endpoints", () => {
});
bookingSeatsRepositoryFixture.create({
- referenceUid: randomString(),
+ referenceUid: `seat-${randomString()}`,
data: {},
booking: {
connect: {
@@ -598,7 +598,7 @@ describe("Slots 2024-04-15 Endpoints", () => {
it("should do a booking for seated event and slot should show attendees count and bookingUid in range format", async () => {
const startTime = "2050-09-05T11:00:00.000Z";
const booking = await bookingsRepositoryFixture.create({
- uid: `booking-uid-${randomString()}`,
+ uid: `booking-uid-seated-${seatedEventTypeId}-${randomString()}`,
title: "booking title",
startTime,
endTime: "2050-09-05T12:00:00.000Z",
@@ -632,7 +632,7 @@ describe("Slots 2024-04-15 Endpoints", () => {
});
bookingSeatsRepositoryFixture.create({
- referenceUid: randomString(),
+ referenceUid: `seat-${randomString()}`,
data: {},
booking: {
connect: {
diff --git a/apps/api/v2/src/modules/slots/slots-2024-09-04/controllers/e2e/user-event-type-slots.controller.e2e-spec.ts b/apps/api/v2/src/modules/slots/slots-2024-09-04/controllers/e2e/user-event-type-slots.controller.e2e-spec.ts
index 0bf0f370f7..3e9d3c99cc 100644
--- a/apps/api/v2/src/modules/slots/slots-2024-09-04/controllers/e2e/user-event-type-slots.controller.e2e-spec.ts
+++ b/apps/api/v2/src/modules/slots/slots-2024-09-04/controllers/e2e/user-event-type-slots.controller.e2e-spec.ts
@@ -787,7 +787,7 @@ describe("Slots 2024-09-04 Endpoints", () => {
it("should do a booking and slot should not be available at that time", async () => {
const startTime = "2050-09-05T11:00:00.000Z";
const booking = await bookingsRepositoryFixture.create({
- uid: `booking-uid-${randomString()}`,
+ uid: `booking-uid-${eventTypeId}`,
title: "booking title",
startTime,
endTime: "2050-09-05T12:00:00.000Z",
@@ -832,7 +832,7 @@ describe("Slots 2024-09-04 Endpoints", () => {
it("should do a booking for seated event and slot should show attendees count and bookingUid", async () => {
const startTime = "2050-09-05T11:00:00.000Z";
const booking = await bookingsRepositoryFixture.create({
- uid: `booking-uid-${randomString()}`,
+ uid: `booking-uid-seated-${seatedEventType.id}-${randomString()}`,
title: "booking title",
startTime,
endTime: "2050-09-05T12:00:00.000Z",
@@ -866,7 +866,7 @@ describe("Slots 2024-09-04 Endpoints", () => {
});
bookingSeatsRepositoryFixture.create({
- referenceUid: randomString(),
+ referenceUid: `seat-${randomString()}`,
data: {},
booking: {
connect: {
@@ -961,7 +961,7 @@ describe("Slots 2024-09-04 Endpoints", () => {
it("should do a booking for seated event and slot should show attendees count and bookingUid and return range format", async () => {
const startTime = "2050-09-05T11:00:00.000Z";
const booking = await bookingsRepositoryFixture.create({
- uid: `booking-uid-${randomString()}`,
+ uid: `booking-uid-seated-range-${seatedEventType.id}-${randomString()}`,
title: "booking title",
startTime,
endTime: "2050-09-05T12:00:00.000Z",
@@ -995,7 +995,7 @@ describe("Slots 2024-09-04 Endpoints", () => {
});
bookingSeatsRepositoryFixture.create({
- referenceUid: randomString(),
+ referenceUid: `seat-${randomString()}`,
data: {},
booking: {
connect: {
@@ -1348,7 +1348,8 @@ describe("Slots 2024-09-04 Endpoints", () => {
advanceTo(newDate);
const slotDuration = 60;
- const slotStartTime = "2050-09-05T10:00:00.000Z";
+ // Use 2050-09-06 to avoid overlap with other tests that reserve 2050-09-05
+ const slotStartTime = "2050-09-06T09:00:00.000Z";
const reserveResponse = await request(app.getHttpServer())
.post(`/v2/slots/reservations`)
.send({
@@ -1377,7 +1378,7 @@ describe("Slots 2024-09-04 Endpoints", () => {
const response = await request(app.getHttpServer())
.get(
- `/v2/slots?eventTypeId=${variableLengthEventType.id}&start=2050-09-05&end=2050-09-09&duration=60`
+ `/v2/slots?eventTypeId=${variableLengthEventType.id}&start=2050-09-05&end=2050-09-10&duration=60`
)
.set(CAL_API_VERSION_HEADER, VERSION_2024_09_04)
.expect(200);
@@ -1390,10 +1391,10 @@ describe("Slots 2024-09-04 Endpoints", () => {
const days = Object.keys(slots);
expect(days.length).toEqual(5);
- const expectedSlotsUTC2050_09_05 = expectedSlotsUTC["2050-09-05"].filter(
+ const expectedSlotsUTC2050_09_06 = expectedSlotsUTC["2050-09-06"].filter(
(slot) => slot.start !== slotStartTime
);
- expect(slots).toEqual({ ...expectedSlotsUTC, "2050-09-05": expectedSlotsUTC2050_09_05 });
+ expect(slots).toEqual({ ...expectedSlotsUTC, "2050-09-06": expectedSlotsUTC2050_09_06 });
const dbSlot = await selectedSlotRepositoryFixture.getByUid(
responseReservedVariableSlot.reservationUid
@@ -1409,7 +1410,7 @@ describe("Slots 2024-09-04 Endpoints", () => {
});
it("request slot contains already existing reserved slot", async () => {
- // Try to reserve 9:45-12:45 when 10:00-11:00 is taken
+ // Try to reserve 8:45-11:45 when 09:00-10:00 on 2050-09-06 is taken
const newSlotStart = DateTime.fromISO(responseReservedVariableSlot.slotStart)
.minus({ minutes: 15 })
.toISO();
diff --git a/apps/api/v2/src/modules/workflows/workflows.repository.ts b/apps/api/v2/src/modules/workflows/workflows.repository.ts
index f8be74f9df..4a341eb32d 100644
--- a/apps/api/v2/src/modules/workflows/workflows.repository.ts
+++ b/apps/api/v2/src/modules/workflows/workflows.repository.ts
@@ -114,7 +114,11 @@ export class WorkflowsRepository {
) {
await updateWorkflow({
ctx: {
- user: { ...user, locale: user?.locale ?? "en" },
+ user: {
+ ...user,
+ locale: user?.locale ?? "en",
+ organizationId: user.profiles?.[0]?.organization?.id ?? null,
+ },
prisma: this.dbWrite.prisma as unknown as PrismaClient,
},
input: data,
@@ -132,7 +136,11 @@ export class WorkflowsRepository {
) {
await updateWorkflow({
ctx: {
- user: { ...user, locale: user?.locale ?? "en" },
+ user: {
+ ...user,
+ locale: user?.locale ?? "en",
+ organizationId: user.profiles?.[0]?.organization?.id ?? null,
+ },
prisma: this.dbWrite.prisma as unknown as PrismaClient,
},
input: data,
diff --git a/apps/web/modules/ee/workflows/components/WorkflowDetailsPage.tsx b/apps/web/modules/ee/workflows/components/WorkflowDetailsPage.tsx
index f564e5490a..8c40078885 100644
--- a/apps/web/modules/ee/workflows/components/WorkflowDetailsPage.tsx
+++ b/apps/web/modules/ee/workflows/components/WorkflowDetailsPage.tsx
@@ -178,6 +178,8 @@ export default function WorkflowDetailsPage(props: Props) {
verifiedAt: SCANNING_WORKFLOW_STEPS ? null : new Date(),
agentId: null,
inboundAgentId: null,
+ autoTranslateEnabled: false,
+ sourceLocale: null,
};
steps?.push(step);
form.setValue("steps", steps);
diff --git a/apps/web/modules/ee/workflows/components/WorkflowStepContainer.tsx b/apps/web/modules/ee/workflows/components/WorkflowStepContainer.tsx
index 150b7a89fc..cbd1bbbf41 100644
--- a/apps/web/modules/ee/workflows/components/WorkflowStepContainer.tsx
+++ b/apps/web/modules/ee/workflows/components/WorkflowStepContainer.tsx
@@ -1,23 +1,22 @@
-import { type TFunction } from "i18next";
+import type { TFunction } from "i18next";
import { useParams, useRouter, useSearchParams } from "next/navigation";
import type { Dispatch, SetStateAction } from "react";
-import { useEffect, useRef, useState, useCallback } from "react";
+import { useCallback, useEffect, useRef, useState } from "react";
import type { UseFormReturn } from "react-hook-form";
import { Controller, useWatch } from "react-hook-form";
import "react-phone-number-input/style.css";
import type { RetellAgentWithDetails } from "@calcom/features/calAIPhone/providers/retellAI";
import { Dialog } from "@calcom/features/components/controlled-dialog";
-import PhoneInput from "@calcom/web/components/phone-input";
import {
- isSMSAction,
- isWhatsappAction,
getTemplateBodyForAction,
- shouldScheduleEmailReminder,
- isSMSOrWhatsappAction,
+ hasCalAIAction,
isCalAIAction,
isFormTrigger,
- hasCalAIAction,
+ isSMSAction,
+ isSMSOrWhatsappAction,
+ isWhatsappAction,
+ shouldScheduleEmailReminder,
} from "@calcom/features/ee/workflows/lib/actionHelperFunctions";
import { DYNAMIC_TEXT_VARIABLES } from "@calcom/features/ee/workflows/lib/constants";
import {
@@ -27,6 +26,7 @@ import {
import emailRatingTemplate from "@calcom/features/ee/workflows/lib/reminders/templates/emailRatingTemplate";
import emailReminderTemplate from "@calcom/features/ee/workflows/lib/reminders/templates/emailReminderTemplate";
import type { FormValues } from "@calcom/features/ee/workflows/lib/types";
+import PhoneInput from "@calcom/web/components/phone-input";
import "@calcom/features/ee/workflows/style/styles.css";
import { SENDER_ID, SENDER_NAME } from "@calcom/lib/constants";
import { formatPhoneNumber } from "@calcom/lib/formatPhoneNumber";
@@ -48,7 +48,7 @@ import { trpc } from "@calcom/trpc/react";
import classNames from "@calcom/ui/classNames";
import { Badge, InfoBadge } from "@calcom/ui/components/badge";
import { Button } from "@calcom/ui/components/button";
-import { DialogContent, DialogFooter, DialogClose } from "@calcom/ui/components/dialog";
+import { DialogClose, DialogContent, DialogFooter } from "@calcom/ui/components/dialog";
import {
Dropdown,
DropdownItem,
@@ -56,26 +56,26 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "@calcom/ui/components/dropdown";
-import { AddVariablesDropdown } from "@calcom/ui/components/editor";
-import { Editor } from "@calcom/ui/components/editor";
-import { CheckboxField } from "@calcom/ui/components/form";
-import { EmailField } from "@calcom/ui/components/form";
-import { TextArea } from "@calcom/ui/components/form";
-import { Input } from "@calcom/ui/components/form";
-import { Label } from "@calcom/ui/components/form";
-import { TextField } from "@calcom/ui/components/form";
-import { Select } from "@calcom/ui/components/form";
-import { MultiSelectCheckbox } from "@calcom/ui/components/form";
+import { AddVariablesDropdown, Editor } from "@calcom/ui/components/editor";
import type { MultiSelectCheckboxesOptionType as Option } from "@calcom/ui/components/form";
+import {
+ CheckboxField,
+ EmailField,
+ Input,
+ Label,
+ MultiSelectCheckbox,
+ Select,
+ TextArea,
+ TextField,
+} from "@calcom/ui/components/form";
import { CircleHelpIcon, InfoIcon, PhoneIcon } from "@coss/ui/icons";
import { SkeletonText } from "@calcom/ui/components/skeleton";
import { showToast } from "@calcom/ui/components/toast";
-import { useHasPaidPlan, useHasActiveTeamPlan } from "@calcom/web/modules/billing/hooks/useHasPaidPlan";
-
+import { useHasActiveTeamPlan, useHasPaidPlan } from "@calcom/web/modules/billing/hooks/useHasPaidPlan";
+import { AgentConfigurationSheet }from "./agent-configuration/AgentConfigurationSheet";
import { TestPhoneCallDialog } from "./TestPhoneCallDialog";
import { TimeTimeUnitInput } from "./TimeTimeUnitInput";
import { WebCallDialog } from "./WebCallDialog";
-import { AgentConfigurationSheet } from "./agent-configuration/AgentConfigurationSheet";
type User = RouterOutputs["viewer"]["me"]["get"];
@@ -477,7 +477,7 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
const verifyEmailCodeMutation = trpc.viewer.workflows.verifyEmailCode.useMutation({
onSuccess: (isVerified) => {
showToast(isVerified ? t("verified_successfully") : t("wrong_code"), "success");
- setEmailVerified(true);
+ setEmailVerified(isVerified);
if (
step &&
form?.formState?.errors?.steps &&
@@ -800,6 +800,15 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
form.setValue(`steps.${step.stepNumber - 1}.agentId`, null);
}
+ if (
+ val.value !== WorkflowActions.EMAIL_ATTENDEE &&
+ val.value !== WorkflowActions.SMS_ATTENDEE
+ ) {
+ form.setValue(`steps.${step.stepNumber - 1}.autoTranslateEnabled`, false, {
+ shouldDirty: true,
+ });
+ }
+
form.setValue(`steps.${step.stepNumber - 1}.sendTo`, null);
form.clearErrors(`steps.${step.stepNumber - 1}.sendTo`);
form.setValue(`steps.${step.stepNumber - 1}.action`, val.value, {
@@ -1433,6 +1442,49 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
/>
)}
+ {(step.action === WorkflowActions.EMAIL_ATTENDEE ||
+ step.action === WorkflowActions.SMS_ATTENDEE) && (
+
+
+ (
+
+ form.setValue(
+ `steps.${step.stepNumber - 1}.autoTranslateEnabled`,
+ e.target.checked,
+ { shouldDirty: true }
+ )
+ }
+ />
+ )}
+ />
+ {!props.user.organizationId && (
+
+ {t("upgrade_to_organizations")}
+
+ )}
+
+ {props.user.organizationId &&
+ form.watch(`steps.${step.stepNumber - 1}.autoTranslateEnabled`) && (
+
+ {t("auto_translate_source_language_hint", {
+ language: new Intl.DisplayNames([i18n.language], { type: "language" }).of(
+ props.user.locale || "en"
+ ),
+ })}
+
+ )}
+
+ )}
{!props.readOnly && (