feat: attributes filter / refactor of user data table (#17014)

* WIP restored from .git cache

* fix exports

* sortable row model

* feat column visibility component

* wip filters with nuqs

* pull in unique values from table into filters

* correctly assign filters via v/f

* inital selection bar refactor

* data-table selection bar + optmistic update of delete

* dynamic link

* migrate member list table to new data-table

* total list shows filtered value > db valuie

* add filters for attributes

* type errors

* make content bigger on lg

* add mb-6 to teams user datatable to match spacing spec

* correctly render multi-badge

* fix: masss asignment optimistic UI

* fix type errors

* remove log

* fix toolbar type error

* chore: Remove debug artifact

* type errors

* Update apps/web/public/static/locales/en/common.json

* use max-w-fit

* chore: Remove unused translation now we don't specify 'mass' in assign

* perf: fix: use the onBlur event to prevent focus loss whilst the list is rerendering

* Move the data-table exports together in the main barrel, then import

* fix exports that were lost in a merge

* fix exports that were lost in a merge

* fix groupteammapping/availbilityslider

* fix overflow problems

* add scrollbar-thin class

* fix type error

* user serverside values for faceted filters

* pass filters to serverside

* filter serverside

* fix team server side filter

* add loaded x of y

* attributes icon change

* correct implementation for text/input attr optimistic

* type check fixes

* fix platform checks

* fix types again

* fix types again

* fix types again

* add use client

* add use client

* fix-types

* fix: Add missing translation in EN

* fix e2e tests via testid

* fix e2e tests via testid

* fix: Member invite popup not popping up

* Update copyInviteLink to new-member-button testid

* Hopefully fix test ids this time

* fix: Use the right buttons on the right pages

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2024-10-15 17:45:11 +00:00
1 parent 0b914bef79
commit 7e44e686e8
43 files changed
+1462 -1049

No files matched your search

@@ -1,5 +1,5 @@
import MembersView from "@calcom/features/ee/organizations/pages/settings/members";
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout";
import SettingsLayout from "@calcom/features/settings/layouts/SettingsLayout";
import PageWrapper from "@components/PageWrapper";
@@ -8,6 +8,10 @@ export {
type PageProps,
} from "@calcom/features/ee/organizations/pages/settings/getServerSidePropsMembers";
export const getLayout = (page: React.ReactElement) => (
<SettingsLayout containerClassName="lg:max-w-screen-2xl">{page}</SettingsLayout>
);
const Page = () => <MembersView />;
Page.getLayout = getLayout;
@@ -49,7 +49,7 @@ export async function acceptTeamOrOrgInvite(page: Page) {
}
async function inviteAnEmail(page: Page, invitedUserEmail: string) {
await page.locator('button:text("Add")').click();
await page.getByTestId("new-organization-member-button").click();
await page.locator('input[name="inviteUser"]').fill(invitedUserEmail);
await submitAndWaitForResponse(page, "/api/trpc/teams/inviteMember?batch=1", {
action: () => page.locator('button:text("Send invite")').click(),
@@ -106,7 +106,7 @@ test.describe("Organization", () => {
const invitedUserEmail = users.trackEmail({ username: "rick", domain: "domain.com" });
// '-domain' because the email doesn't match orgAutoAcceptEmail
const usernameDerivedFromEmail = `${invitedUserEmail.split("@")[0]}-domain`;
await inviteAnEmail(page, invitedUserEmail);
await inviteAnEmail(page, invitedUserEmail, true);
await expectUserToBeAMemberOfTeam({
page,
teamId: team.id,
@@ -164,7 +164,7 @@ test.describe("Organization", () => {
await test.step("By invite link", async () => {
await page.goto(`/settings/teams/${team.id}/members`);
const inviteLink = await copyInviteLink(page);
const inviteLink = await copyInviteLink(page, true);
const email = users.trackEmail({ username: "rick", domain: "domain.com" });
// '-domain' because the email doesn't match orgAutoAcceptEmail
const usernameDerivedFromEmail = `${email.split("@")[0]}-domain`;
@@ -333,7 +333,7 @@ test.describe("Organization", () => {
await page.goto(`/settings/teams/${team.id}/members`);
const invitedUserEmail = users.trackEmail({ username: "rick", domain: "example.com" });
const usernameDerivedFromEmail = invitedUserEmail.split("@")[0];
await inviteAnEmail(page, invitedUserEmail);
await inviteAnEmail(page, invitedUserEmail, true);
await expectUserToBeAMemberOfTeam({
page,
teamId: team.id,
@@ -391,7 +391,7 @@ test.describe("Organization", () => {
await test.step("By invite link", async () => {
await page.goto(`/settings/teams/${team.id}/members`);
const inviteLink = await copyInviteLink(page);
const inviteLink = await copyInviteLink(page, true);
const email = users.trackEmail({ username: "rick", domain: "example.com" });
// '-domain' because the email doesn't match orgAutoAcceptEmail
const usernameDerivedFromEmail = `${email.split("@")[0]}`;
@@ -481,8 +481,12 @@ export async function signupFromEmailInviteLink({
await signupPage.close();
}
async function inviteAnEmail(page: Page, invitedUserEmail: string) {
await page.locator('button:text("Add")').click();
async function inviteAnEmail(page: Page, invitedUserEmail: string, teamPage?: boolean) {
if (teamPage) {
await page.getByTestId("new-member-button").click();
} else {
await page.getByTestId("new-organization-member-button").click();
}
await page.locator('input[name="inviteUser"]').fill(invitedUserEmail);
const submitPromise = page.waitForResponse("/api/trpc/teams/inviteMember?batch=1");
await page.locator('button:text("Send invite")').click();
@@ -558,8 +562,12 @@ function assertInviteLink(inviteLink: string | null | undefined): asserts invite
if (!inviteLink) throw new Error("Invite link not found");
}
async function copyInviteLink(page: Page) {
await page.locator('button:text("Add")').click();
async function copyInviteLink(page: Page, teamPage?: boolean) {
if (teamPage) {
await page.getByTestId("new-member-button").click();
} else {
await page.getByTestId("new-organization-member-button").click();
}
const inviteLink = await getInviteLink(page);
return inviteLink;
}
@@ -25,7 +25,7 @@ test.describe("Team", () => {
username: "rick",
domain: `domain-${Date.now()}.com`,
});
await page.locator(`button:text("${t("add")}")`).click();
await page.getByTestId("new-member-button").click();
await page.locator('input[name="inviteUser"]').fill(invitedUserEmail);
await page.locator(`button:text("${t("send_invite")}")`).click();
const inviteLink = await expectInvitationEmailToBeReceived(
@@ -74,7 +74,8 @@ test.describe("Team", () => {
email: `user-invite-${Date.now()}@domain.com`,
password: "P4ssw0rd!",
});
await page.locator(`button:text("${t("add")}")`).click();
await page.getByTestId("new-member-button").click();
const inviteLink = await getInviteLink(page);
const context = await browser.newContext();
@@ -105,7 +106,7 @@ test.describe("Team", () => {
username: "rick",
domain: `example.com`,
});
await page.locator(`button:text("${t("add")}")`).click();
await page.getByTestId("new-member-button").click();
await page.locator('input[name="inviteUser"]').fill(invitedUserEmail);
await page.locator(`button:text("${t("send_invite")}")`).click();
await expectInvitationEmailToBeReceived(
@@ -2599,6 +2599,7 @@
"new_option": "New option",
"update_profile": "Update member",
"attribute_updated_successfully": "Attribute updated successfully",
"attribute_deleted_successfully": "Attribute deleted successfully",
"attributes_edited_successfully": "Attributes edited successfully",
"attribute_meta_description": "Manage attributes for your team members",
"attributes_edit_description": "Edit attributes for your team members",
@@ -2644,6 +2645,9 @@
"month_to_date": "month to date",
"year_to_date": "year to date",
"custom_range": "custom range",
"show_all_columns": "Show all columns",
"toggle_columns": "Toggle columns",
"no_columns_found": "No columns found",
"salesforce_create_record_as": "On booking, add events on and new attendees as:",
"salesforce_lead": "Lead",
"salesforce_contact_under_account": "Contact under an account",