feat: add redirect uris for booking success, cancel and reschedule for a platform user (#14518)

* update typings

* update prisma to include redirect uris for booking success, cancel and reschedule

* update migrations

* add input for uris and send them back in custom hook

* display new uris

* add type for enabling emails

* update prisma schema and add migrations

* chores

* fixup

* chore: fix style, remove console log, rewrite migration

---------

Co-authored-by: Morgan Vernay <morgan@cal.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
This commit is contained in:
Rajiv Sahal
2024-04-11 17:46:57 +00:00
committed by GitHub
co-authored by Morgan Vernay Morgan
parent af0cc5c899
commit 7836aeacc6
7 changed files with 129 additions and 4 deletions
@@ -1,4 +1,4 @@
import { IsArray, IsOptional, IsString } from "class-validator";
import { IsArray, IsBoolean, IsOptional, IsString } from "class-validator";
export class UpdateOAuthClientInput {
@IsOptional()
@@ -13,4 +13,20 @@ export class UpdateOAuthClientInput {
@IsOptional()
@IsString({ each: true })
redirectUris?: string[] = [];
@IsOptional()
@IsString()
bookingRedirectUri?: string;
@IsOptional()
@IsString()
bookingCancelRedirectUri?: string;
@IsOptional()
@IsString()
bookingRescheduleRedirectUri?: string;
@IsOptional()
@IsBoolean()
areEmailsEnabled?: boolean;
}
@@ -11,6 +11,10 @@ type OAuthClientCardProps = {
name: string;
logo?: Avatar;
redirectUris: string[];
bookingRedirectUri: string | null;
bookingCancelRedirectUri: string | null;
bookingRescheduleRedirectUri: string | null;
areEmailsEnabled: boolean;
permissions: number;
lastItem: boolean;
id: string;
@@ -23,13 +27,19 @@ export const OAuthClientCard = ({
name,
logo,
redirectUris,
bookingRedirectUri,
bookingCancelRedirectUri,
bookingRescheduleRedirectUri,
permissions,
id,
secret,
lastItem,
onDelete,
isLoading,
areEmailsEnabled,
}: OAuthClientCardProps) => {
console.log(areEmailsEnabled);
const clientPermissions = Object.values(PERMISSIONS_GROUPED_MAP).map((value, index) => {
let permissionsMessage = "";
const hasReadPermission = hasPermission(permissions, value.read);
@@ -110,8 +120,26 @@ export const OAuthClientCard = ({
<span className="font-semibold">Redirect uris: </span>
{redirectUris.map((item, index) => (redirectUris.length === index + 1 ? `${item}` : `${item}, `))}
</div>
{bookingRedirectUri && (
<div className="flex gap-1 text-sm">
<span className="font-semibold">Booking redirect uri: </span> {bookingRedirectUri}
</div>
)}
{bookingRescheduleRedirectUri && (
<div className="flex gap-1 text-sm">
<span className="font-semibold">Booking reschedule uri: </span> {bookingRescheduleRedirectUri}
</div>
)}
{bookingCancelRedirectUri && (
<div className="flex gap-1 text-sm">
<span className="font-semibold">Booking cancel uri: </span> {bookingCancelRedirectUri}
</div>
)}
<div className="flex gap-1 text-sm">
<span className="text-sm font-semibold">Emails enabled:</span> {areEmailsEnabled ? "Yes" : "No"}
</div>
</div>
<div className="flex items-center">
<div className="flex items-start">
<Button
className="bg-red-500 text-white hover:bg-red-600"
loading={isLoading}
@@ -6,7 +6,7 @@ import { useForm, useFieldArray } from "react-hook-form";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { PERMISSIONS_GROUPED_MAP } from "@calcom/platform-constants/permissions";
import { showToast } from "@calcom/ui";
import { Meta, Button, TextField, Label } from "@calcom/ui";
import { Meta, Button, TextField, Label, Tooltip } from "@calcom/ui";
import { useCreateOAuthClient } from "@lib/hooks/settings/organizations/platform/oauth-clients/usePersistOAuthClient";
@@ -27,6 +27,10 @@ type FormValues = {
redirectUris: {
uri: string;
}[];
bookingRedirectUri?: string;
bookingCancelRedirectUri?: string;
bookingRescheduleRedirectUri?: string;
areEmailsEnabled?: boolean;
};
export const OAuthClientForm: FC = () => {
@@ -83,6 +87,10 @@ export const OAuthClientForm: FC = () => {
permissions: userPermissions,
// logo: data.logo,
redirectUris: userRedirectUris,
bookingRedirectUri: data.bookingRedirectUri,
bookingCancelRedirectUri: data.bookingCancelRedirectUri,
bookingRescheduleRedirectUri: data.bookingRescheduleRedirectUri,
areEmailsEnabled: data.areEmailsEnabled,
});
};
@@ -209,6 +217,47 @@ export const OAuthClientForm: FC = () => {
)}
/>
</div> */}
<div className="mt-6">
<Tooltip content="URL of your booking page">
<TextField
type="url"
label="Booking redirect uri"
className="w-[100%]"
{...register("bookingRedirectUri")}
/>
</Tooltip>
</div>
<div className="mt-6">
<Tooltip content="URL of the page where your users can cancel their booking">
<TextField
type="url"
label="Booking cancel redirect uri"
className="w-[100%]"
{...register("bookingCancelRedirectUri")}
/>
</Tooltip>
</div>
<div className="mt-6">
<Tooltip content="URL of the page where your users can reschedule their booking">
<TextField
type="url"
label="Booking reschedule redirect uri"
className="w-[100%]"
{...register("bookingRescheduleRedirectUri")}
/>
</Tooltip>
</div>
<div className="mt-6">
<input
{...register("areEmailsEnabled")}
id="areEmailsEnabled"
className="bg-default border-default h-4 w-4 shrink-0 cursor-pointer rounded-[4px] border ring-offset-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed"
type="checkbox"
/>
<label htmlFor="areEmailsEnabled" className="cursor-pointer px-2 text-base font-semibold">
Enable emails
</label>
</div>
<div className="mt-6">
<div className="flex justify-between">
<h1 className="text-base font-semibold underline">Permissions</h1>
@@ -62,10 +62,15 @@ export const OAuthClients = () => {
<>
<div className="border-subtle rounded-b-lg border border-t-0">
{data.map((client, index) => {
console.log(client);
return (
<OAuthClientCard
name={client.name}
redirectUris={client.redirectUris}
bookingRedirectUri={client.bookingRedirectUri}
bookingRescheduleRedirectUri={client.bookingRescheduleRedirectUri}
bookingCancelRedirectUri={client.bookingCancelRedirectUri}
permissions={client.permissions}
key={index}
lastItem={data.length === index + 1}
@@ -73,6 +78,7 @@ export const OAuthClients = () => {
secret={client.secret}
isLoading={isDeleting}
onDelete={handleDelete}
areEmailsEnabled={client.areEmailsEnabled}
/>
);
})}
+17 -1
View File
@@ -1,4 +1,4 @@
import { IsArray, IsNumber, IsOptional, IsString } from "class-validator";
import { IsArray, IsNumber, IsOptional, IsBoolean, IsString } from "class-validator";
import { z } from "zod";
export class CreateOAuthClientInput {
@@ -15,6 +15,22 @@ export class CreateOAuthClientInput {
@IsNumber()
permissions!: number;
@IsOptional()
@IsString()
bookingRedirectUri?: string;
@IsOptional()
@IsString()
bookingCancelRedirectUri?: string;
@IsOptional()
@IsString()
bookingRescheduleRedirectUri?: string;
@IsOptional()
@IsBoolean()
areEmailsEnabled?: boolean;
}
export class DeleteOAuthClientInput {
@@ -0,0 +1,5 @@
-- AlterTable
ALTER TABLE "PlatformOAuthClient" ADD COLUMN "areEmailsEnabled" BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN "bookingCancelRedirectUri" TEXT,
ADD COLUMN "bookingRedirectUri" TEXT,
ADD COLUMN "bookingRescheduleRedirectUri" TEXT;
+5
View File
@@ -1228,6 +1228,11 @@ model PlatformOAuthClient {
refreshToken RefreshToken[]
authorizationTokens PlatformAuthorizationToken[]
bookingRedirectUri String?
bookingCancelRedirectUri String?
bookingRescheduleRedirectUri String?
areEmailsEnabled Boolean @default(false)
createdAt DateTime @default(now())
}