-
Notifications
You must be signed in to change notification settings - Fork 6.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: orgs trigger alert when loading a calendar and no availability …
…is (#14796) * Install upstash redis again - base of noficationSender * Setup email handler etc and use tasker * Remove logs * Sending emails and correct format and spacing in emails * Update email styles * Update email styles * add switch to enable feature * fix: reset tasker types - will fix in new PR * WIP: test suite * WIP: test suite * Add redis service and add WIP mock test * Add tests for redis service and fix lpush * More working tests * More working tests * fix: type error + typo * update export to match i18n next mock * (debug) push for debug * Fix: fixed hosting in mocks * Add common.json i18n * add better test descriptions * reset mocks after each test to allow concurancy * Remove redundant RedisService.test.ts * Rename and remove redundant isAdmin check * Rename to .d.ts * Add key versioning to constructRedisKey * Update packages/trpc/server/routers/viewer/slots/handleNotificationWhenNoSlots.ts Co-authored-by: Hariom Balhara <hariombalhara@gmail.com> * Update packages/trpc/server/routers/viewer/slots/handleNotificationWhenNoSlots.ts Co-authored-by: Hariom Balhara <hariombalhara@gmail.com> * Add try/catch * fix breaking when option === undefined * Add missing await to Promise.all * Rename data stored in redis to save space * Include thrown error in logs --------- Co-authored-by: Hariom Balhara <hariombalhara@gmail.com> Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
- Loading branch information
1 parent
88f4dd2
commit c478f73
Showing
19 changed files
with
508 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
packages/emails/src/templates/OrganizationAdminNoSlots.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import type { TFunction } from "next-i18next"; | ||
import { Trans } from "next-i18next"; | ||
|
||
import { BaseEmailHtml, CallToAction } from "../components"; | ||
|
||
export type OrganizationAdminNoSlotsEmailInput = { | ||
language: TFunction; | ||
to: { | ||
email: string; | ||
}; | ||
user: string; | ||
slug: string; | ||
startTime: string; | ||
editLink: string; | ||
}; | ||
|
||
export const OrganizationAdminNoSlotsEmail = ( | ||
props: OrganizationAdminNoSlotsEmailInput & Partial<React.ComponentProps<typeof BaseEmailHtml>> | ||
) => { | ||
return ( | ||
<BaseEmailHtml subject={`No availability found for ${props.user}`}> | ||
<p | ||
style={{ | ||
fontWeight: 600, | ||
fontSize: "32px", | ||
lineHeight: "38px", | ||
}}> | ||
<>{props.language("org_admin_no_slots|heading", { name: props.user })}</> | ||
</p> | ||
<p style={{ fontWeight: 400, fontSize: "16px", lineHeight: "24px" }}> | ||
<Trans i18nKey="org_admin_no_slots|content" values={{ username: props.user, slug: props.slug }}> | ||
Hello Organization Admins, | ||
<br /> | ||
<br /> | ||
Please note: It has been brought to our attention that {props.user} has not had any availability | ||
when a user has visited {props.user}/{props.slug} | ||
<br /> | ||
<br /> | ||
There’s a few reasons why this could be happening | ||
<br /> | ||
The user does not have any calendars connected | ||
<br /> | ||
Their schedules attached to this event are not enabled | ||
</Trans> | ||
</p> | ||
<div style={{ marginTop: "3rem", marginBottom: "0.75rem" }}> | ||
<CallToAction | ||
label={props.language("org_admin_no_slots|cta")} | ||
href={props.editLink} | ||
endIconName="linkIcon" | ||
/> | ||
</div> | ||
</BaseEmailHtml> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
packages/emails/templates/organization-admin-no-slots-email.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import type { TFunction } from "next-i18next"; | ||
|
||
import { APP_NAME } from "@calcom/lib/constants"; | ||
|
||
import renderEmail from "../src/renderEmail"; | ||
import BaseEmail from "./_base-email"; | ||
|
||
export type OrganizationAdminNoSlotsEmailInput = { | ||
language: TFunction; | ||
to: { | ||
email: string; | ||
}; | ||
user: string; | ||
slug: string; | ||
startTime: string; | ||
editLink: string; | ||
}; | ||
|
||
export default class OrganizationAdminNoSlotsEmail extends BaseEmail { | ||
adminNoSlots: OrganizationAdminNoSlotsEmailInput; | ||
|
||
constructor(adminNoSlots: OrganizationAdminNoSlotsEmailInput) { | ||
super(); | ||
this.name = "SEND_ORG_ADMIN_NO_SLOTS_EMAIL_EMAIL"; | ||
this.adminNoSlots = adminNoSlots; | ||
} | ||
|
||
protected async getNodeMailerPayload(): Promise<Record<string, unknown>> { | ||
return { | ||
from: `${APP_NAME} <${this.getMailerOptions().from}>`, | ||
to: this.adminNoSlots.to.email, | ||
subject: this.adminNoSlots.language("org_admin_no_slots|subject", { name: this.adminNoSlots.user }), | ||
html: await renderEmail("OrganizationAdminNoSlotsEmail", this.adminNoSlots), | ||
text: this.getTextBody(), | ||
}; | ||
} | ||
|
||
protected getTextBody(): string { | ||
return ` | ||
Hi Admins, | ||
It has been brought to our attention that ${this.adminNoSlots.user} has not had availability users have visited ${this.adminNoSlots.user}/${this.adminNoSlots.slug}. | ||
There’s a few reasons why this could be happening | ||
The user does not have any calendars connected | ||
Their schedules attached to this event are not enabled | ||
We recommend checking their availability to resolve this | ||
`; | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
packages/features/ee/organizations/pages/components/NoSlotsNotificationSwitch.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { useState } from "react"; | ||
|
||
import { useLocale } from "@calcom/lib/hooks/useLocale"; | ||
import type { RouterOutputs } from "@calcom/trpc"; | ||
import { trpc } from "@calcom/trpc"; | ||
import { SettingsToggle, showToast } from "@calcom/ui"; | ||
|
||
interface GeneralViewProps { | ||
currentOrg: RouterOutputs["viewer"]["organizations"]["listCurrent"]; | ||
isAdminOrOwner: boolean; | ||
} | ||
|
||
export const NoSlotsNotificationSwitch = ({ currentOrg, isAdminOrOwner }: GeneralViewProps) => { | ||
const { t } = useLocale(); | ||
const utils = trpc.useUtils(); | ||
const [notificationActive, setNotificationActive] = useState( | ||
!!currentOrg.organizationSettings.adminGetsNoSlotsNotification | ||
); | ||
|
||
const mutation = trpc.viewer.organizations.update.useMutation({ | ||
onSuccess: async () => { | ||
showToast(t("settings_updated_successfully"), "success"); | ||
}, | ||
onError: () => { | ||
showToast(t("error_updating_settings"), "error"); | ||
}, | ||
onSettled: () => { | ||
utils.viewer.organizations.listCurrent.invalidate(); | ||
}, | ||
}); | ||
|
||
if (!isAdminOrOwner) return null; | ||
|
||
return ( | ||
<> | ||
<SettingsToggle | ||
toggleSwitchAtTheEnd={true} | ||
title={t("organization_no_slots_notification_switch_title")} | ||
disabled={mutation?.isPending} | ||
description={t("organization_no_slots_notification_switch_description")} | ||
checked={notificationActive} | ||
onCheckedChange={(checked) => { | ||
mutation.mutate({ | ||
adminGetsNoSlotsNotification: checked, | ||
}); | ||
setNotificationActive(checked); | ||
}} | ||
switchContainerClassName="mt-6" | ||
/> | ||
</> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export interface IRedisService { | ||
get: <TData>(key: string) => Promise<TData | null>; | ||
|
||
set: <TData>(key: string, value: TData) => Promise<"OK" | TData | null>; | ||
|
||
expire: (key: string, seconds: number) => Promise<0 | 1>; | ||
|
||
lrange: <TResult = string>(key: string, start: number, end: number) => Promise<TResult[]>; | ||
|
||
lpush: <TData>(key: string, ...elements: TData[]) => Promise<number>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { Redis } from "@upstash/redis"; | ||
|
||
import type { IRedisService } from "./IRedisService"; | ||
|
||
export class RedisService implements IRedisService { | ||
private redis: Redis; | ||
|
||
constructor() { | ||
this.redis = Redis.fromEnv(); | ||
} | ||
|
||
async get<TData>(key: string): Promise<TData | null> { | ||
return this.redis.get(key); | ||
} | ||
|
||
async set<TData>(key: string, value: TData): Promise<"OK" | TData | null> { | ||
// Implementation for setting value in Redis | ||
return this.redis.set(key, value); | ||
} | ||
|
||
async expire(key: string, seconds: number): Promise<0 | 1> { | ||
// Implementation for setting expiration time for key in Redis | ||
return this.redis.expire(key, seconds); | ||
} | ||
|
||
async lrange<TResult = string>(key: string, start: number, end: number): Promise<TResult[]> { | ||
return this.redis.lrange(key, start, end); | ||
} | ||
|
||
async lpush<TData>(key: string, ...elements: TData[]): Promise<number> { | ||
return this.redis.lpush(key, elements); | ||
} | ||
} |
2 changes: 2 additions & 0 deletions
2
packages/prisma/migrations/20240429100018_add_org_admin_no_slots_notification/migration.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
-- AlterTable | ||
ALTER TABLE "OrganizationSettings" ADD COLUMN "adminGetsNoSlotsNotification" BOOLEAN NOT NULL DEFAULT false; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.