fix: rescheduled value DB update on reschedule and insights view cancelleds (#11474)

pull/11701/head^2
alannnc 2023-10-04 12:10:10 -07:00 committed by GitHub
parent 53655a43eb
commit c554779490
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 287 additions and 4 deletions

View File

@ -0,0 +1,239 @@
import { expect } from "@playwright/test";
import { randomString } from "@calcom/lib/random";
import prisma from "@calcom/prisma";
import { test } from "./lib/fixtures";
test.describe.configure({ mode: "parallel" });
const createTeamsAndMembership = async (userIdOne: number, userIdTwo: number) => {
const teamOne = await prisma.team.create({
data: {
name: "test-insights",
slug: `test-insights-${Date.now()}-${randomString(5)}}`,
},
});
const teamTwo = await prisma.team.create({
data: {
name: "test-insights-2",
slug: `test-insights-2-${Date.now()}-${randomString(5)}}`,
},
});
if (!userIdOne || !userIdTwo || !teamOne || !teamTwo) {
throw new Error("Failed to create test data");
}
// create memberships
await prisma.membership.create({
data: {
userId: userIdOne,
teamId: teamOne.id,
accepted: true,
role: "ADMIN",
},
});
await prisma.membership.create({
data: {
teamId: teamTwo.id,
userId: userIdOne,
accepted: true,
role: "ADMIN",
},
});
await prisma.membership.create({
data: {
teamId: teamOne.id,
userId: userIdTwo,
accepted: true,
role: "MEMBER",
},
});
await prisma.membership.create({
data: {
teamId: teamTwo.id,
userId: userIdTwo,
accepted: true,
role: "MEMBER",
},
});
return { teamOne, teamTwo };
};
test.afterAll(async ({ users }) => {
await users.deleteAll();
});
test.describe("Insights", async () => {
test("should be able to go to insights as admins", async ({ page, users }) => {
const user = await users.create();
const userTwo = await users.create();
await createTeamsAndMembership(user.id, userTwo.id);
await user.apiLogin();
// go to insights page
await page.goto("/insights");
await page.waitForLoadState("networkidle");
// expect url to have isAll and TeamId in query params
expect(page.url()).toContain("isAll=false");
expect(page.url()).toContain("teamId=");
});
test("should be able to go to insights as members", async ({ page, users }) => {
const user = await users.create();
const userTwo = await users.create();
await userTwo.apiLogin();
await createTeamsAndMembership(user.id, userTwo.id);
// go to insights page
await page.goto("/insights");
await page.waitForLoadState("networkidle");
// expect url to have isAll and TeamId in query params
expect(page.url()).toContain("isAll=false");
expect(page.url()).not.toContain("teamId=");
});
test("team select filter should have 2 teams and your account option only as member", async ({
page,
users,
}) => {
const user = await users.create();
const userTwo = await users.create();
await user.apiLogin();
await createTeamsAndMembership(user.id, userTwo.id);
// go to insights page
await page.goto("/insights");
await page.waitForLoadState("networkidle");
// get div from team select filter with this class flex flex-col gap-0.5 [&>*:first-child]:mt-1 [&>*:last-child]:mb-1
await page.getByTestId("dashboard-shell").getByText("Team: test-insights").click();
await page
.locator('div[class="flex flex-col gap-0.5 [&>*:first-child]:mt-1 [&>*:last-child]:mb-1"]')
.click();
const teamSelectFilter = await page.locator(
'div[class="hover:bg-muted flex items-center py-2 pl-3 pr-2.5 hover:cursor-pointer"]'
);
await expect(teamSelectFilter).toHaveCount(3);
});
test("Insights Organization should have isAll option true", async ({ users, page }) => {
const owner = await users.create(undefined, {
hasTeam: true,
isUnpublished: true,
isOrg: true,
hasSubteam: true,
});
await owner.apiLogin();
await page.goto("/insights");
await page.waitForLoadState("networkidle");
await page.getByTestId("dashboard-shell").getByText("All").nth(1).click();
const teamSelectFilter = await page.locator(
'div[class="hover:bg-muted flex items-center py-2 pl-3 pr-2.5 hover:cursor-pointer"]'
);
await expect(teamSelectFilter).toHaveCount(4);
});
test("should have all option in team-and-self filter as admin", async ({ page, users }) => {
const owner = await users.create();
const member = await users.create();
await createTeamsAndMembership(owner.id, member.id);
await owner.apiLogin();
await page.goto("/insights");
// get div from team select filter with this class flex flex-col gap-0.5 [&>*:first-child]:mt-1 [&>*:last-child]:mb-1
await page.getByTestId("dashboard-shell").getByText("Team: test-insights").click();
await page
.locator('div[class="flex flex-col gap-0.5 [&>*:first-child]:mt-1 [&>*:last-child]:mb-1"]')
.click();
const teamSelectFilter = await page.locator(
'div[class="hover:bg-muted flex items-center py-2 pl-3 pr-2.5 hover:cursor-pointer"]'
);
await expect(teamSelectFilter).toHaveCount(3);
});
test("should be able to switch between teams and self profile for insights", async ({ page, users }) => {
const owner = await users.create();
const member = await users.create();
await createTeamsAndMembership(owner.id, member.id);
await owner.apiLogin();
await page.goto("/insights");
// get div from team select filter with this class flex flex-col gap-0.5 [&>*:first-child]:mt-1 [&>*:last-child]:mb-1
await page.getByTestId("dashboard-shell").getByText("Team: test-insights").click();
await page
.locator('div[class="flex flex-col gap-0.5 [&>*:first-child]:mt-1 [&>*:last-child]:mb-1"]')
.click();
const teamSelectFilter = await page.locator(
'div[class="hover:bg-muted flex items-center py-2 pl-3 pr-2.5 hover:cursor-pointer"]'
);
await expect(teamSelectFilter).toHaveCount(3);
// switch to self profile
await page.getByTestId("dashboard-shell").getByText("Your Account").click();
// switch to team 1
await page.getByTestId("dashboard-shell").getByText("test-insights").nth(0).click();
// switch to team 2
await page.getByTestId("dashboard-shell").getByText("test-insights-2").click();
});
test("should be able to switch between memberUsers", async ({ page, users }) => {
const owner = await users.create();
const member = await users.create();
await createTeamsAndMembership(owner.id, member.id);
await owner.apiLogin();
await page.goto("/insights");
await page.getByText("Add filter").click();
await page.getByRole("button", { name: "User" }).click();
// <div class="flex select-none truncate font-medium" data-state="closed">People</div>
await page.locator('div[class="flex select-none truncate font-medium"]').getByText("People").click();
await page
.locator('div[class="hover:bg-muted flex items-center py-2 pl-3 pr-2.5 hover:cursor-pointer"]')
.nth(0)
.click();
await page.waitForLoadState("networkidle");
await page
.locator('div[class="hover:bg-muted flex items-center py-2 pl-3 pr-2.5 hover:cursor-pointer"]')
.nth(1)
.click();
await page.waitForLoadState("networkidle");
// press escape button to close the filter
await page.keyboard.press("Escape");
await page.getByRole("button", { name: "Clear" }).click();
// expect for "Team: test-insight" text in page
expect(await page.locator("text=Team: test-insights").isVisible()).toBeTruthy();
});
});

View File

@ -2148,6 +2148,7 @@ async function handler(
id: originalRescheduledBooking.id,
},
data: {
rescheduled: true,
status: BookingStatus.CANCELLED,
},
});

View File

@ -114,17 +114,19 @@ export function FiltersProvider({ children }: { children: React.ReactNode }) {
selectedFilter,
isAll,
dateRange,
initialConfig,
} = newConfigFilters;
const [startTime, endTime] = dateRange || [null, null];
const newSearchParams = new URLSearchParams(searchParams);
const newSearchParams = new URLSearchParams(searchParams.toString());
function setParamsIfDefined(key: string, value: string | number | boolean | null | undefined) {
if (value !== undefined && value !== null) newSearchParams.set(key, value.toString());
}
setParamsIfDefined("memberUserId", selectedMemberUserId);
setParamsIfDefined("teamId", selectedTeamId);
setParamsIfDefined("userId", selectedUserId);
setParamsIfDefined("teamId", selectedTeamId || initialConfig?.teamId);
setParamsIfDefined("userId", selectedUserId || initialConfig?.userId);
setParamsIfDefined("eventTypeId", selectedEventTypeId);
setParamsIfDefined("isAll", isAll);
setParamsIfDefined("isAll", isAll || initialConfig?.isAll);
setParamsIfDefined("startTime", startTime?.toISOString());
setParamsIfDefined("endTime", endTime?.toISOString());
setParamsIfDefined("filter", selectedFilter?.[0]);

View File

@ -22,6 +22,11 @@ export const TeamAndSelfList = () => {
const { data, isSuccess } = trpc.viewer.insights.teamListForUser.useQuery(undefined, {
// Teams don't change that frequently
refetchOnWindowFocus: false,
trpc: {
context: {
skipBatch: true,
},
},
});
useEffect(() => {
@ -48,6 +53,7 @@ export const TeamAndSelfList = () => {
} else if (session.data?.user.id) {
// default to user
setConfigFilters({
selectedUserId: session.data?.user.id,
initialConfig: {
teamId: null,
userId: session.data?.user.id,

View File

@ -0,0 +1,35 @@
-- View: public.BookingsTimeStatus
-- DROP VIEW public."BookingsTimeStatus";
CREATE OR REPLACE VIEW public."BookingTimeStatus"
AS
SELECT "Booking".id,
"Booking".uid,
"Booking"."eventTypeId",
"Booking".title,
"Booking".description,
"Booking"."startTime",
"Booking"."endTime",
"Booking"."createdAt",
"Booking".location,
"Booking".paid,
"Booking".status,
"Booking".rescheduled,
"Booking"."userId",
"et"."teamId",
"et"."length" as "eventLength",
CASE
WHEN "Booking".rescheduled IS TRUE THEN 'rescheduled'::text
WHEN "Booking".status = 'cancelled'::"BookingStatus" AND "Booking".rescheduled IS NULL THEN 'cancelled'::text
WHEN "Booking"."endTime" < now() THEN 'completed'::text
WHEN "Booking"."endTime" > now() THEN 'uncompleted'::text
ELSE NULL::text
END AS "timeStatus",
"et"."parentId" as "eventParentId"
FROM "Booking"
LEFT JOIN "EventType" et ON "Booking"."eventTypeId" = et.id
LEFT JOIN "Membership" mb ON "mb"."userId" = "Booking"."userId";