feat: Organizations users calendar cache (#9498)
* Initial commit * Adding feature flag * feat: Orgs Schema Changing `scopedMembers` to `orgUsers` (#9209) * Change scopedMembers to orgMembers * Change to orgUsers * Letting duplicate slugs for teams to support orgs * Covering null on unique clauses * Supporting having the orgId in the session cookie * feat: organization event type filter (#9253) Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in> * Missing changes to support orgs schema changes * feat: Onboarding process to create an organization (#9184) * Desktop first banner, mobile pending * Removing dead code and img * WIP * Adds Email verification template+translations for organizations (#9202) * First step done * Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding * Step 2 done, avatar not working * Covering null on unique clauses * Onboarding admins step * Last step to create teams * Moving change password handler, improving verifying code flow * Clearing error before submitting * Reverting email testing api changes * Reverting having the banner for now * Consistent exported components * Remove unneeded files from banner * Removing uneeded code * Fixing avatar selector * Using meta component for head/descr * Missing i18n strings * Feedback * Making an org avatar (temp) * Check for subteams slug clashes with usernames * Fixing create teams onsuccess * feedback * Making sure we check requestedSlug now --------- Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> * feat: [CAL-1816] Organization subdomain support (#9345) * Desktop first banner, mobile pending * Removing dead code and img * WIP * Adds Email verification template+translations for organizations (#9202) * First step done * Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding * Step 2 done, avatar not working * Covering null on unique clauses * Onboarding admins step * Last step to create teams * Moving change password handler, improving verifying code flow * Clearing error before submitting * Reverting email testing api changes * Reverting having the banner for now * Consistent exported components * Remove unneeded files from banner * Removing uneeded code * Fixing avatar selector * Using meta component for head/descr * Missing i18n strings * Feedback * Making an org avatar (temp) * Check for subteams slug clashes with usernames * Fixing create teams onsuccess * Covering users and subteams, excluding non-org users * Unpublished teams shows correctly * Create subdomain in Vercel * feedback * Renaming Vercel env vars * Vercel domain check before creation * Supporting cal-staging.com * Change to have vercel detect it * vercel domain check data message error * Remove check domain * Making sure we check requestedSlug now * Feedback and unneeded code * Reverting unneeded changes * Unneeded changes --------- Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> * Vercel subdomain creation in PROD only * Making sure we let localhost still work * Feedback * Type check fixes * feat: Organization branding in side menu (#9279) * Desktop first banner, mobile pending * Removing dead code and img * WIP * Adds Email verification template+translations for organizations (#9202) * First step done * Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding * Step 2 done, avatar not working * Covering null on unique clauses * Onboarding admins step * Last step to create teams * Moving change password handler, improving verifying code flow * Clearing error before submitting * Reverting email testing api changes * Reverting having the banner for now * Consistent exported components * Remove unneeded files from banner * Removing uneeded code * Fixing avatar selector * Org branding provider used in shell sidebar * Using meta component for head/descr * Missing i18n strings * Feedback * Making an org avatar (temp) * Using org avatar (temp) * Not showing org logo if not set * User onboarding with org branding (slug) * Check for subteams slug clashes with usernames * Fixing create teams onsuccess * feedback * Feedback * Org public profile * Public profiles for team event types * Added setup profile alert * Using org avatar on subteams avatar * Making sure we show the set up profile on org only * Profile username availability rely on org hook * Update apps/web/pages/team/[slug].tsx Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> * Update apps/web/pages/team/[slug].tsx Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> --------- Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> * feat: Organization support for event types page (#9449) * Desktop first banner, mobile pending * Removing dead code and img * WIP * Adds Email verification template+translations for organizations (#9202) * First step done * Merge branch 'feat/organizations-onboarding' of github.com:calcom/cal.com into feat/organizations-onboarding * Step 2 done, avatar not working * Covering null on unique clauses * Onboarding admins step * Last step to create teams * Moving change password handler, improving verifying code flow * Clearing error before submitting * Reverting email testing api changes * Reverting having the banner for now * Consistent exported components * Remove unneeded files from banner * Removing uneeded code * Fixing avatar selector * Org branding provider used in shell sidebar * Using meta component for head/descr * Missing i18n strings * Feedback * Making an org avatar (temp) * Using org avatar (temp) * Not showing org logo if not set * User onboarding with org branding (slug) * Check for subteams slug clashes with usernames * Fixing create teams onsuccess * feedback * Feedback * Org public profile * Public profiles for team event types * Added setup profile alert * Using org avatar on subteams avatar * Processing orgs and children as profile options * Reverting change not belonging to this PR * Making sure we show the set up profile on org only * Removing console.log * Comparing memberships to choose the highest one --------- Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> * Type errors * Refactor and type fixes * Update orgDomains.ts * Feedback * Reverting * NIT * fix issue getting org slug from domain * Improving orgDomains util * Host comes with port * Update useRouterQuery.ts * add org users cache-calendar * fix typo * re-use the simple user calendar-cache page * Apply suggestions from code review * Update packages/core/CalendarManager.ts * type fixes --------- Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in> Co-authored-by: Leo Giovanetti <hello@leog.me> Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com> Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com> Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Co-authored-by: zomars <zomars@me.com>
This commit is contained in:
co-authored by
sean-brydon
Leo Giovanetti
Joe Au-Yeung
Udit Takkar
zomars
parent
eadca3b2c0
commit
4284a4d16d
@@ -0,0 +1 @@
|
||||
export { getStaticProps, getStaticPaths, default } from "./index";
|
||||
@@ -185,13 +185,20 @@ export const getCachedResults = async (
|
||||
* On development environment it takes a long time because Next must compiles the whole page.
|
||||
* @param username
|
||||
* @param month A string representing year and month using YYYY-MM format
|
||||
* @param organizationSlug A string with the organization slug
|
||||
*/
|
||||
const getNextCache = async (username: string, month: string): Promise<EventBusyDate[][]> => {
|
||||
const getNextCache = async (
|
||||
username: string,
|
||||
month: string,
|
||||
organizationSlug?: string | null
|
||||
): Promise<EventBusyDate[][]> => {
|
||||
let localCache: EventBusyDate[][] = [];
|
||||
const { NODE_ENV } = process.env;
|
||||
const cacheDir = `${NODE_ENV === "development" ? NODE_ENV : process.env.BUILD_ID}`;
|
||||
const baseUrl = `${WEBAPP_URL}/_next/data/${cacheDir}/en`;
|
||||
const url = `${baseUrl}/${username}/calendar-cache/${month}.json?user=${username}&month=${month}`;
|
||||
const url = organizationSlug
|
||||
? `${baseUrl}/${username}/calendar-cache/${month}/${organizationSlug}.json?user=${username}&month=${month}&orgSlug=${organizationSlug}`
|
||||
: `${baseUrl}/${username}/calendar-cache/${month}.json?user=${username}&month=${month}`;
|
||||
try {
|
||||
localCache = await fetch(url)
|
||||
.then((r) => r.json())
|
||||
@@ -218,17 +225,18 @@ const getMonths = (dateFrom: string, dateTo: string): string[] => {
|
||||
return months;
|
||||
};
|
||||
|
||||
const createCalendarCachePage = (username: string, month: string): void => {
|
||||
const createCalendarCachePage = (username: string, month: string, organizationSlug?: string | null): void => {
|
||||
// No need to wait for this, the purpose is to force re-validation every second as indicated
|
||||
// in page getStaticProps.
|
||||
fetch(`${WEBAPP_URL}/${username}/calendar-cache/${month}`).catch(console.log);
|
||||
fetch(`${WEBAPP_URL}/${username}/calendar-cache/${month}/${organizationSlug}`).catch(console.log);
|
||||
};
|
||||
export const getBusyCalendarTimes = async (
|
||||
username: string,
|
||||
withCredentials: CredentialPayload[],
|
||||
dateFrom: string,
|
||||
dateTo: string,
|
||||
selectedCalendars: SelectedCalendar[]
|
||||
selectedCalendars: SelectedCalendar[],
|
||||
organizationSlug?: string | null
|
||||
) => {
|
||||
let results: EventBusyDate[][] = [];
|
||||
const months = getMonths(dateFrom, dateTo);
|
||||
@@ -239,16 +247,16 @@ export const getBusyCalendarTimes = async (
|
||||
// Add 14 hours from the start date to avoid problems in UTC+ time zones.
|
||||
const endDate = dayjs(dateTo).endOf("month").add(14, "hours").format();
|
||||
results = await getCalendarsEvents(withCredentials, startDate, endDate, selectedCalendars);
|
||||
logger.info("Generating calendar cache in background");
|
||||
console.log("Generating calendar cache in background");
|
||||
// on cold start the calendar cache page generated in the background
|
||||
Promise.all(months.map((month) => createCalendarCachePage(username, month)));
|
||||
Promise.all(months.map((month) => createCalendarCachePage(username, month, organizationSlug)));
|
||||
} else {
|
||||
if (months.length === 1) {
|
||||
results = await getNextCache(username, dayjs(dateFrom).format("YYYY-MM"));
|
||||
results = await getNextCache(username, dayjs(dateFrom).format("YYYY-MM"), organizationSlug);
|
||||
} else {
|
||||
// if dateFrom and dateTo is from different months get cache by each month
|
||||
const data: EventBusyDate[][][] = await Promise.all(
|
||||
months.map((month) => getNextCache(username, month))
|
||||
months.map((month) => getNextCache(username, month, organizationSlug))
|
||||
);
|
||||
results = data.flat(1);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ export async function getBusyTimes(params: {
|
||||
credentials: Credential[];
|
||||
userId: number;
|
||||
username: string;
|
||||
organizationSlug?: string | null | undefined;
|
||||
eventTypeId?: number;
|
||||
startTime: string;
|
||||
beforeEventBuffer?: number;
|
||||
@@ -27,6 +28,7 @@ export async function getBusyTimes(params: {
|
||||
eventTypeId,
|
||||
startTime,
|
||||
endTime,
|
||||
organizationSlug,
|
||||
beforeEventBuffer,
|
||||
afterEventBuffer,
|
||||
selectedCalendars,
|
||||
@@ -128,7 +130,8 @@ export async function getBusyTimes(params: {
|
||||
credentials,
|
||||
startTime,
|
||||
endTime,
|
||||
selectedCalendars
|
||||
selectedCalendars,
|
||||
organizationSlug
|
||||
);
|
||||
const endConnectedCalendarsGet = performance.now();
|
||||
logger.debug(
|
||||
|
||||
@@ -74,6 +74,11 @@ const getUser = (where: Prisma.UserWhereInput) =>
|
||||
select: {
|
||||
...availabilityUserSelect,
|
||||
credentials: true,
|
||||
organization: {
|
||||
select: {
|
||||
slug: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -162,6 +167,7 @@ export async function getUserAvailability(
|
||||
eventTypeId,
|
||||
userId: user.id,
|
||||
username: `${user.username}`,
|
||||
organizationSlug: initialData?.user?.organization?.slug,
|
||||
beforeEventBuffer,
|
||||
afterEventBuffer,
|
||||
selectedCalendars: user.selectedCalendars,
|
||||
|
||||
@@ -293,6 +293,11 @@ const getEventTypesFromDB = async (eventTypeId: number) => {
|
||||
select: {
|
||||
credentials: true,
|
||||
...userSelect.select,
|
||||
organization: {
|
||||
select: {
|
||||
slug: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -318,7 +323,11 @@ const getEventTypesFromDB = async (eventTypeId: number) => {
|
||||
};
|
||||
};
|
||||
|
||||
type IsFixedAwareUser = User & { isFixed: boolean; credentials: Credential[] };
|
||||
type IsFixedAwareUser = User & {
|
||||
isFixed: boolean;
|
||||
credentials: Credential[];
|
||||
organization: { slug: string };
|
||||
};
|
||||
|
||||
async function ensureAvailableUsers(
|
||||
eventType: Awaited<ReturnType<typeof getEventTypesFromDB>> & {
|
||||
@@ -694,6 +703,11 @@ async function handler(
|
||||
...userSelect.select,
|
||||
credentials: true, // Don't leak to client
|
||||
metadata: true,
|
||||
organization: {
|
||||
select: {
|
||||
slug: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
: eventType.hosts?.length
|
||||
|
||||
@@ -115,6 +115,11 @@ export async function getEventType(input: TGetScheduleInputSchema) {
|
||||
select: {
|
||||
credentials: true, // Don't leak credentials to the client
|
||||
...availabilityUserSelect,
|
||||
organization: {
|
||||
select: {
|
||||
slug: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -123,6 +128,11 @@ export async function getEventType(input: TGetScheduleInputSchema) {
|
||||
select: {
|
||||
credentials: true, // Don't leak credentials to the client
|
||||
...availabilityUserSelect,
|
||||
organization: {
|
||||
select: {
|
||||
slug: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -156,6 +166,11 @@ export async function getDynamicEventType(input: TGetScheduleInputSchema) {
|
||||
allowDynamicBooking: true,
|
||||
credentials: true, // Don't leak credentials to the client
|
||||
...availabilityUserSelect,
|
||||
organization: {
|
||||
select: {
|
||||
slug: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const isDynamicAllowed = !users.some((user) => !user.allowDynamicBooking);
|
||||
|
||||
@@ -27,6 +27,11 @@ export const getMemberAvailabilityHandler = async ({ ctx, input }: GetMemberAvai
|
||||
select: {
|
||||
credentials: true, // needed for getUserAvailability
|
||||
...availabilityUserSelect,
|
||||
organization: {
|
||||
select: {
|
||||
slug: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user