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:
co-authored by
Morgan Vernay
Morgan
parent
af0cc5c899
commit
7836aeacc6
@@ -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;
|
||||
}
|
||||
|
||||
+29
-1
@@ -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}
|
||||
|
||||
+50
-1
@@ -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}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -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;
|
||||
@@ -1228,6 +1228,11 @@ model PlatformOAuthClient {
|
||||
refreshToken RefreshToken[]
|
||||
authorizationTokens PlatformAuthorizationToken[]
|
||||
|
||||
bookingRedirectUri String?
|
||||
bookingCancelRedirectUri String?
|
||||
bookingRescheduleRedirectUri String?
|
||||
areEmailsEnabled Boolean @default(false)
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user