Skip to content

Commit

Permalink
Merge branch 'master' into production
Browse files Browse the repository at this point in the history
  • Loading branch information
RuntimeTerror10 committed May 10, 2024
2 parents 4b68146 + a75681d commit cd62b8f
Show file tree
Hide file tree
Showing 16 changed files with 312 additions and 235 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React, { useState } from "react";
import { IoMdClose } from "@react-icons/all-files/io/IoMdClose";
import { Col, Drawer, Row } from "antd";
import { AddMembersTable } from "./components/AddMembersTable/AddMembersTable";
import "./addMembersDrawer.scss";

interface AppMembersDrawerProps {
isOpen: boolean;
onClose: () => void;
}

export const AppMembersDrawer: React.FC<AppMembersDrawerProps> = ({ isOpen, onClose }) => {
const [searchValue, setSearchValue] = useState("");

return (
<Drawer
placement="right"
onClose={onClose}
open={isOpen}
width={640}
closeIcon={null}
mask={false}
className="billing-team-members-drawer"
>
<Row className="billing-team-members-drawer-header w-full" justify="space-between" align="middle">
<Col className="billing-team-members-drawer-header_title">Add members in billing team</Col>
<Col>
<IoMdClose onClick={onClose} />
</Col>
</Row>
<Col className="billing-team-members-drawer-body">
<AddMembersTable searchValue={searchValue} setSearchValue={setSearchValue} />
</Col>
</Drawer>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React, { useMemo } from "react";
import { OrgMembersTable } from "features/settings/components/OrgMembers/components/OrgMembersTable/OrgMembersTable";
import { useSelector } from "react-redux";
import { getUserAuthDetails } from "store/selectors";
import { AddMembersTableActions } from "./components/AddMembersTableActions/AddMembersTableActions";
import { useFetchOrgMembers } from "features/settings/components/OrgMembers/hooks/useFetchOrganizationMembers";

interface AddMembersTableProps {
searchValue: string;
setSearchValue: (value: string) => void;
}

export const AddMembersTable: React.FC<AddMembersTableProps> = ({ searchValue, setSearchValue }) => {
const user = useSelector(getUserAuthDetails);
const { isLoading, organizationMembers } = useFetchOrgMembers();

const searchedMembers = useMemo(() => {
if (!organizationMembers) return [];
return organizationMembers?.filter((member: any) => {
return member?.email?.includes(searchValue) && member?.email !== user?.details?.profile?.email;
});
}, [organizationMembers, searchValue, user?.details?.profile?.email]);

return (
<OrgMembersTable
isLoading={isLoading}
searchValue={searchValue}
setSearchValue={setSearchValue}
members={searchedMembers}
actions={(member) => <AddMembersTableActions member={member} />}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ import Logger from "lib/logger";
import { toast } from "utils/Toast";
import { trackBillingTeamActionClicked, trackBillingTeamMemberAdded } from "features/settings/analytics";
import { addUsersToBillingTeam } from "backend/billing";
import { OrgMember } from "features/settings/components/OrgMembers/types";

export const OrgTableActions: React.FC<{ record: any }> = ({ record }) => {
export const AddMembersTableActions: React.FC<{ member: OrgMember }> = ({ member }) => {
const { billingId } = useParams();
const billingTeamMembers = useSelector(getBillingTeamMembers(billingId));
const user = useSelector(getUserAuthDetails);
const isUserAdded = useMemo(
() => Object.values(billingTeamMembers ?? {}).some((member) => member.email === record.email),
[billingTeamMembers, record.email]
() => Object.values(billingTeamMembers ?? {}).some((billingTeamMember) => billingTeamMember.email === member.email),
[billingTeamMembers, member.email]
);

const [isAddingUser, setIsAddingUser] = useState(false);
Expand All @@ -30,16 +31,16 @@ export const OrgTableActions: React.FC<{ record: any }> = ({ record }) => {

const handleAddUserToBillingTeam = useCallback(() => {
setIsAddingUser(true);
addUsersToBillingTeam(billingId, [record.email])
addUsersToBillingTeam(billingId, [member.email])
.then(() => {
trackBillingTeamMemberAdded(record.email, billingId);
trackBillingTeamMemberAdded(member.email, billingId);
})
.catch((e) => {
Logger.log(e);
toast.error("Could not add user to billing team, Please contact support");
})
.finally(() => setIsAddingUser(false));
}, [billingId, record.email]);
}, [billingId, member.email]);

return (
<>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useEffect, useState } from "react";
import { Col, Drawer, Row } from "antd";
import { Col } from "antd";
import { TeamPlanDetails } from "./components/TeamPlanDetails";
import { BillingTeamMembers } from "./components/BillingTeamMembers";
import { BillingInvoiceTable } from "./components/BillingInvoiceTable";
Expand All @@ -11,9 +11,7 @@ import { BillingTeamRoles } from "../../../types";
import { isCompanyEmail } from "utils/FormattingHelper";
import { trackBillingTeamViewed } from "features/settings/analytics";
import { BillingInformation } from "./components/BillingInformation";
import { OrgMembersTable } from "features/settings/components/OrgMembersTable";
import { IoMdClose } from "@react-icons/all-files/io/IoMdClose";
import "./index.scss";
import { AppMembersDrawer } from "./components/AddMembersDrawer/AddMembersDrawer";

export const MyBillingTeamDetails: React.FC = () => {
const { billingId } = useParams();
Expand Down Expand Up @@ -68,34 +66,7 @@ export const MyBillingTeamDetails: React.FC = () => {
</Col>
)}

<Drawer
placement="right"
onClose={() => setIsMembersDrawerOpen(false)}
open={isMembersDrawerOpen}
width={640}
closeIcon={null}
mask={false}
className="billing-team-members-drawer"
>
<Row className="billing-team-members-drawer-header w-full" justify="space-between" align="middle">
<Col className="billing-team-members-drawer-header_title">Add members in billing team</Col>
<Col>
<IoMdClose onClick={() => setIsMembersDrawerOpen(false)} />
</Col>
</Row>
<Col className="billing-team-members-drawer-body">
<OrgMembersTable source="add_members_section" />
</Col>
<Row className="mt-8 billing-team-members-drawer-help" justify="space-between" align="middle">
<Col>
Couldn't find member?{" "}
<a className="external-link" href="mailto:[email protected]">
Contact us
</a>
, and we'll assist you in adding your team members.
</Col>
</Row>
</Drawer>
<AppMembersDrawer isOpen={isMembersDrawerOpen} onClose={() => setIsMembersDrawerOpen(false)} />
</div>
</div>
);
Expand Down
2 changes: 2 additions & 0 deletions app/src/features/settings/components/BillingTeam/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const BillingTeamContainer: React.FC = () => {
: `You need to login to view this billing team`
);
dispatch(
// @ts-ignore
actions.toggleActiveModal({
modalName: "authModal",
newValue: true,
Expand Down Expand Up @@ -67,6 +68,7 @@ export const BillingTeamContainer: React.FC = () => {
type="primary"
onClick={() => {
dispatch(
// @ts-ignore
actions.toggleActiveModal({
modalName: "authModal",
newValue: true,
Expand Down
41 changes: 41 additions & 0 deletions app/src/features/settings/components/OrgMembers/OrgMembers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useMemo, useState } from "react";
import { Col } from "antd";
import { useSelector } from "react-redux";
import { getUserAuthDetails } from "store/selectors";
import { OrgMembersTable } from "./components/OrgMembersTable/OrgMembersTable";
import { getDomainFromEmail } from "utils/FormattingHelper";
import { capitalize } from "lodash";
import { useFetchOrgMembers } from "./hooks/useFetchOrganizationMembers";
import "./orgMembers.scss";

export const OrgMembersView = () => {
const user = useSelector(getUserAuthDetails);
const domain = getDomainFromEmail(user?.details?.profile?.email)?.split(".")[0];
const { isLoading, organizationMembers } = useFetchOrgMembers();
const [searchValue, setSearchValue] = useState("");

const searchedMembers = useMemo(() => {
if (!organizationMembers) return [];
return organizationMembers?.filter((member: any) => {
return member?.email?.includes(searchValue) && member?.email !== user?.details?.profile?.email;
});
}, [organizationMembers, searchValue, user?.details?.profile?.email]);

return (
<Col className="org-members-table-container">
<Col className="org-members-table-wrapper">
<Col className="my-billing-team-title" style={{ alignSelf: "start" }}>
{capitalize(domain)} Members
</Col>
<div className="mt-16">
<OrgMembersTable
isLoading={isLoading}
members={searchedMembers}
searchValue={searchValue}
setSearchValue={setSearchValue}
/>
</div>
</Col>
</Col>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React, { ReactNode, useMemo } from "react";
import { Col, Empty, Input, Row, Table, TableProps } from "antd";
import { SearchOutlined } from "@ant-design/icons";
import { OrgMember } from "../../types";
import "./orgMembersTable.scss";

interface OrgMembersTableProps {
isLoading: boolean;
searchValue: string;
members: OrgMember[];
setSearchValue: (value: string) => void;
actions?: (member: OrgMember) => ReactNode;
}

export const OrgMembersTable: React.FC<OrgMembersTableProps> = ({
searchValue,
setSearchValue,
members,
actions,
isLoading,
}) => {
const columns: TableProps<OrgMember>["columns"] = useMemo(
() => [
{
title: "Member",
key: "member",
width: 350,
render: (_: any, member) => {
return (
<Row align="middle" gutter={8}>
<Col>
<img
className="org-member-avatar"
src={
member?.photoURL.length
? member?.photoURL
: "https://www.gravatar.com/avatar/00000000000000000000000000000000?d=mp&f=y"
}
alt={member?.email}
/>
</Col>
<Col className="org-member-email">{member?.email}</Col>
</Row>
);
},
defaultSortOrder: "ascend",
showSorterTooltip: false,
sorter: {
compare: (a, b) => a.email.localeCompare(b.email),
},
},
{
title: "",
key: "action",
render: (_: any, member) => {
return actions?.(member);
},
},
],

[actions]
);

return (
<Col>
<Col className="org-member-table">
<Col className="org-member-table-header">
<Input
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
placeholder="Search members"
className="org-member-table-header-input"
suffix={<SearchOutlined />}
/>
</Col>
<Table
className="billing-table"
dataSource={members}
columns={columns}
pagination={false}
scroll={{ y: "74vh" }}
loading={isLoading}
locale={{
emptyText: (
<Empty
image={Empty.PRESENTED_IMAGE_SIMPLE}
description={
!members.length ? (
<>
Couldn't find member?{" "}
<a className="external-link" href="mailto:[email protected]">
Contact us
</a>{" "}
and we'll assist you in adding your team members.
</>
) : (
"No member found"
)
}
/>
),
}}
/>
</Col>
</Col>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,23 @@
&-input {
width: 224px;
border-radius: 4px;
border: 1px solid var(--surface-2);
background: var(--background);
border: 1px solid var(--requestly-color-white-t-20);
background: transparent;

.ant-input {
background: var(--background);
background: transparent;

&::placeholder {
color: var(--surface-3);
color: var(--requestly-color-text-placeholder);
}
}
}
}

input.ant-input:not(.ant-input-sm):not(.ant-input-lg) {
padding: 0;
}

.ant-table-column-sorter {
display: none;
}
Expand Down
Loading

0 comments on commit cd62b8f

Please sign in to comment.