Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MPDX-7829 Ministries Have Self Service MPDX Donor/Account Removal Capability #887

Merged
merged 42 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
5594ccf
Set accountLists organization search from local storage
caleballdrin Mar 1, 2024
2270b75
Get userId and lastSyncedAt from searchOrganizationsAccountLists
caleballdrin Mar 1, 2024
4aaf01e
Add textfield functionality to Confirmation modal
caleballdrin Mar 1, 2024
18e1178
Allow disableHover on infinitelist
caleballdrin Mar 1, 2024
ab188c1
Add removeUser, deleteUser and deleteAccountList functionality
caleballdrin Mar 1, 2024
fac0802
Refactor confirm dialog state
caleballdrin Mar 2, 2024
2e0706a
Add tooltip and search instructions
caleballdrin Mar 2, 2024
33b1c5f
Adjust admin contact list formatting, tooltip and instructions
caleballdrin Mar 2, 2024
8c33c7c
Merge branch 'main' of https://github.com/CruGlobal/mpdx-react into a…
caleballdrin Mar 6, 2024
c248328
Add new anonymize mutation and confirmation dialog
caleballdrin Mar 7, 2024
8447b10
Fix tests
caleballdrin Mar 7, 2024
230cebf
Fix tests
caleballdrin Mar 7, 2024
234ee38
Memoize the selectedOrg and make PR changes
caleballdrin Mar 8, 2024
5563486
Refactor confirm dialog
caleballdrin Mar 9, 2024
ea2d32e
Change Confirm dialog props
caleballdrin Mar 9, 2024
2fbd921
Adjust contact spacing
caleballdrin Mar 9, 2024
d0207a5
Add tests, useMemo and syntax changes
caleballdrin Mar 12, 2024
36fd02b
Remove contact delete functionality
caleballdrin Mar 14, 2024
6bc5d62
Update delete confirmation message and styles
caleballdrin Mar 14, 2024
4acc6bd
Use <Trans> and add more tests
caleballdrin Apr 5, 2024
aa614da
Merge branch 'main' into account-removal
caleballdrin Apr 5, 2024
0fac26f
Fix cache deleting and local storage
caleballdrin Apr 10, 2024
a40be30
Change tests
caleballdrin Apr 10, 2024
41d5b94
Add translating and update the cache with updateQuery
caleballdrin Apr 15, 2024
09b9034
Merge branch 'main' into account-removal
caleballdrin Apr 18, 2024
f9eb33b
Fix email type
caleballdrin Apr 18, 2024
6e9e5ae
Get org from localstorage in a useEffect
caleballdrin Apr 30, 2024
e7df841
Merge branch 'main' into account-removal
caleballdrin Apr 30, 2024
2a5a42b
Fix type
caleballdrin Apr 30, 2024
d9683eb
Change warning message
caleballdrin May 21, 2024
4837441
Fix graphql errors from Task/Phases
caleballdrin May 29, 2024
6db838e
Merge branch 'main' into account-removal
caleballdrin May 30, 2024
28ee8c9
Revert "Fix graphql errors from Task/Phases"
caleballdrin May 30, 2024
f9197f0
Fix tests and lint errors
caleballdrin May 30, 2024
2740fdc
Show account list user contact info
caleballdrin Jun 6, 2024
506a535
Merge branch 'main' into account-removal
caleballdrin Jun 26, 2024
01fa696
Dont show delete button if accountList or user is in more than 1 orga…
caleballdrin Jun 26, 2024
426bbf9
Add test for user contact info
caleballdrin Aug 19, 2024
7b2526e
Merge branch 'main' into account-removal
caleballdrin Aug 19, 2024
d548c63
Add error state tests
caleballdrin Aug 19, 2024
e6281a5
Move modals into separate components, review changes
caleballdrin Aug 20, 2024
3d17d66
Test that contacts, users and coaches are removed
caleballdrin Aug 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { Dispatch, SetStateAction } from 'react';

export type OrganizationsContextType = {
selectedOrganizationId: string;
selectedOrganizationName: string;
search: string;
setSearch: Dispatch<SetStateAction<string>>;
clearFilters: () => void;
Expand All @@ -12,16 +13,30 @@ export const OrganizationsContext =
interface OrganizationsContextProviderProps {
children: React.ReactNode;
selectedOrganizationId: string;
selectedOrganizationName: string;
search: string;
setSearch: Dispatch<SetStateAction<string>>;
clearFilters: () => void;
}
export const OrganizationsContextProvider: React.FC<
OrganizationsContextProviderProps
> = ({ children, selectedOrganizationId, search, setSearch, clearFilters }) => {
> = ({
children,
selectedOrganizationId,
selectedOrganizationName,
search,
setSearch,
clearFilters,
}) => {
return (
<OrganizationsContext.Provider
value={{ selectedOrganizationId, search, setSearch, clearFilters }}
value={{
selectedOrganizationId,
selectedOrganizationName,
search,
setSearch,
clearFilters,
}}
>
{children}
</OrganizationsContext.Provider>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { ReactElement, useState } from 'react';
import React, { ReactElement, useEffect, useState } from 'react';
import PersonSearchIcon from '@mui/icons-material/PersonSearch';
import {
Autocomplete,
Box,
InputAdornment,
Skeleton,
TextField,
Tooltip,
} from '@mui/material';
import { styled } from '@mui/material/styles';
import useMediaQuery from '@mui/material/useMediaQuery';
Expand All @@ -29,23 +30,39 @@

const AccountListsOrganizations = (): ReactElement => {
const { t } = useTranslation();
const [search, setSearch] = useState('');
const matches = useMediaQuery('(max-width:600px)');
const [selectedOrganization, setSelectedOrganization] = useState<
SettingsOrganizationFragment | null | undefined
>();
>(null);

const [search, setSearch] = useState('');
const isNarrowScreen = useMediaQuery('(max-width:600px)');
caleballdrin marked this conversation as resolved.
Show resolved Hide resolved

const { data } = useOrganizationsQuery();
const organizations = data?.getOrganizations.organizations;
const contactSearch = useDebouncedValue(search, 1000);

const clearFilters = () => {
setSearch('');
setSelectedOrganization(undefined);
};

useEffect(() => {
if (!window?.localStorage) {
return;

Check warning on line 50 in pages/accountLists/[accountListId]/settings/organizations/accountLists.page.tsx

View check run for this annotation

Codecov / codecov/patch

pages/accountLists/[accountListId]/settings/organizations/accountLists.page.tsx#L50

Added line #L50 was not covered by tests
}
const savedOrg = window.localStorage.getItem('admin-org');
savedOrg && setSelectedOrganization(JSON.parse(savedOrg));
}, []);

const handleSelectedOrgChange = (organization): void => {
const org = organizations?.find((org) => org?.id === organization);
setSelectedOrganization(org);
org && window.localStorage.setItem(`admin-org`, JSON.stringify(org));
};

return (
<OrganizationsContextProvider
selectedOrganizationId={selectedOrganization?.id ?? ''}
selectedOrganizationName={selectedOrganization?.name ?? ''}
search={contactSearch}
setSearch={setSearch}
clearFilters={clearFilters}
Expand All @@ -64,31 +81,39 @@
{organizations?.length && (
<HeaderAndDropdown>
<Box>
<TextField
label={t('Search Account Lists')}
value={search}
onChange={(e) => setSearch(e.target.value)}
fullWidth
multiline
inputProps={{ 'aria-label': 'Search Account Lists' }}
style={{
width: matches ? '150px' : '250px',
}}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<PersonSearchIcon />
</InputAdornment>
),
}}
/>
{selectedOrganization && (
<Tooltip
title={t('Search by name, email or account number')}
placement={'bottom'}
arrow
>
<TextField
label={t('Search Account Lists')}
value={search}
onChange={(e) => {
setSearch(e.target.value);
}}
fullWidth
inputProps={{ 'aria-label': 'Search Account Lists' }}
style={{
width: isNarrowScreen ? '150px' : '250px',
}}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<PersonSearchIcon />
</InputAdornment>
),
}}
/>
</Tooltip>
)}
</Box>
<Box>
<Autocomplete
style={{
width: matches ? '150px' : '250px',
width: isNarrowScreen ? '150px' : '350px',
}}
autoSelect
autoHighlight
options={organizations?.map((org) => org?.id) || []}
getOptionLabel={(orgId) =>
Expand All @@ -104,12 +129,9 @@
/>
)}
value={selectedOrganization?.id ?? null}
onChange={(_, organization): void => {
const org = organizations?.find(
(org) => org?.id === organization,
);
setSelectedOrganization(org);
}}
onChange={(_, organization): void =>
handleSelectedOrgChange(organization)
}
/>
</Box>
</HeaderAndDropdown>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
InputAdornment,
Skeleton,
TextField,
Tooltip,
} from '@mui/material';
import { styled } from '@mui/material/styles';
import useMediaQuery from '@mui/material/useMediaQuery';
Expand All @@ -30,7 +31,7 @@ const HeaderAndDropdown = styled(Box)(() => ({
const OrganizationsContacts = (): ReactElement => {
const { t } = useTranslation();
const [search, setSearch] = useState('');
const matches = useMediaQuery('(max-width:600px)');
const isNarrowScreen = useMediaQuery('(max-width:600px)');
const [selectedOrganization, setSelectedOrganization] = useState<
SettingsOrganizationFragment | null | undefined
>();
Expand All @@ -47,6 +48,7 @@ const OrganizationsContacts = (): ReactElement => {
return (
<OrganizationsContextProvider
selectedOrganizationId={selectedOrganization?.id ?? ''}
selectedOrganizationName={selectedOrganization?.name ?? ''}
search={contactSearch}
setSearch={setSearch}
clearFilters={clearFilters}
Expand All @@ -66,32 +68,36 @@ const OrganizationsContacts = (): ReactElement => {
<HeaderAndDropdown>
<Box>
{selectedOrganization && (
<TextField
label={t('Search Contacts')}
value={search}
onChange={(e) => setSearch(e.target.value)}
fullWidth
multiline
inputProps={{ 'aria-label': 'Search Contacts' }}
style={{
width: matches ? '150px' : '250px',
}}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<PersonSearchIcon />
</InputAdornment>
),
}}
/>
<Tooltip
title={t('Search by name, phone, email or partner number')}
placement={'bottom'}
arrow
>
<TextField
label={t('Search Contacts')}
value={search}
onChange={(e) => setSearch(e.target.value)}
fullWidth
inputProps={{ 'aria-label': 'Search Contacts' }}
style={{
width: isNarrowScreen ? '150px' : '250px',
}}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<PersonSearchIcon />
</InputAdornment>
),
}}
/>
</Tooltip>
)}
</Box>
<Box>
<Autocomplete
style={{
width: matches ? '150px' : '250px',
width: isNarrowScreen ? '150px' : '350px',
}}
autoSelect
autoHighlight
options={organizations?.map((org) => org?.id) || []}
getOptionLabel={(orgId) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type SearchOrganizationsAccountListsResponse {
type OrganizationsAccountList {
id: ID!
name: String!
organizationCount: Int
designationAccounts: [AccountListDesignationAccounts]
accountListUsers: [AccountListUsers]
accountListUsersInvites: [AccountListInvites]
Expand All @@ -36,7 +37,10 @@ type AccountListUsers {
userFirstName: String
userLastName: String
allowDeletion: Boolean
userId: ID
lastSyncedAt: String
userEmailAddresses: [AccountListEmailAddresses]
organizationCount: Int
}

type AccountListInvites {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@
type AccountListIncludedTypes = {
id: string;
type: string;
attributes: any;

Check warning on line 46 in pages/api/Schema/Settings/Organizations/SearchOrganizationsAccountLists/datahandler.ts

View workflow job for this annotation

GitHub Actions / eslint

Unexpected any. Specify a different type
relationships: any;

Check warning on line 47 in pages/api/Schema/Settings/Organizations/SearchOrganizationsAccountLists/datahandler.ts

View workflow job for this annotation

GitHub Actions / eslint

Unexpected any. Specify a different type
};

interface SearchOrganizationsAccountListsReturned {
Expand Down Expand Up @@ -76,6 +76,7 @@
type SearchOrganizationsAccountListsAccountList = {
name: string;
id: string;
organizationCount: number;
designationAccounts: {
displayName: string;
id: string;
Expand All @@ -89,6 +90,9 @@
userFirstName: string;
userLastName: string;
allowDeletion: boolean;
userId: string;
lastSyncedAt: string;
caleballdrin marked this conversation as resolved.
Show resolved Hide resolved
organizationCount: number;
userEmailAddresses: EmailAddress[];
}[];
accountListUsersInvites: AccountListInvite[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ type SearchOrganizationsContactsResponse {

type OrganizationsContact {
id: ID!
allowDeletion: Boolean!
name: String!
squareAvatar: String
people: [ContactPeople]!
Expand Down Expand Up @@ -67,8 +66,8 @@ type ContactPeopleAccountLists {

type ContactPeopleAccountListsUsers {
id: ID
firstName: String
lastName: String
emailAddresses: [ContactPeopleEmailAddresses]
userFirstName: String
userLastName: String
userEmailAddresses: [ContactPeopleEmailAddresses]
phoneNumbers: [ContactPeoplePhoneNumbers]
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
type ContactIncludedTypes = {
id: string;
type: string;
attributes: any;

Check warning on line 45 in pages/api/Schema/Settings/Organizations/SearchOrganizationsContacts/datahandler.ts

View workflow job for this annotation

GitHub Actions / eslint

Unexpected any. Specify a different type
relationships: any;

Check warning on line 46 in pages/api/Schema/Settings/Organizations/SearchOrganizationsContacts/datahandler.ts

View workflow job for this annotation

GitHub Actions / eslint

Unexpected any. Specify a different type
};

interface SearchOrganizationsContactsReturned {
Expand Down Expand Up @@ -82,9 +82,9 @@
name: string;
accountListUsers: {
id: string;
firstName: string;
lastName: string;
emailAddresses: EmailAddress[];
userFirstName: string;
userLastName: string;
userEmailAddresses: EmailAddress[];
}[];
};
addresses: {
Expand Down
8 changes: 4 additions & 4 deletions pages/api/graphql-rest.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1202,7 +1202,7 @@ class MpdxRestApi extends RESTDataSource {
) {
const include =
'people,people.email_addresses,people.phone_numbers,addresses,account_list,' +
'account_list.account_list_users,account_list.account_list_users.email_addresses';
'account_list.account_list_users,account_list.account_list_users.user_email_addresses';
const filters =
`filter[organization_id]=${organizationId}` +
`&filter[wildcard_search]=${search}` +
Expand All @@ -1213,7 +1213,7 @@ class MpdxRestApi extends RESTDataSource {
'&fields[email_addresses]=email,primary,historic' +
'&fields[phone_numbers]=number,primary,historic' +
'&fields[account_lists]=name,account_list_users' +
'&fields[account_list_users]=first_name,last_name,email_addresses' +
'&fields[account_list_users]=user_first_name,user_last_name,user_email_addresses' +
'&fields[addresses]=primary_mailing_address,street,city,state,postal_code';

const data: SearchOrganizationsContactsResponse = await this.get(
Expand Down Expand Up @@ -1256,9 +1256,9 @@ class MpdxRestApi extends RESTDataSource {
: '';
const filters = `filter[wildcard_search]=${search}` + organizationIdFilter;
const fields =
'fields[account_lists]=name,account_list_coaches,account_list_users,account_list_invites,designation_accounts' +
'fields[account_lists]=name,account_list_coaches,account_list_users,account_list_invites,designation_accounts,organization_count' +
'&fields[account_list_coaches]=coach_first_name,coach_last_name,coach_email_addresses' +
'&fields[account_list_users]=user_first_name,user_last_name,user_email_addresses,allow_deletion' +
'&fields[account_list_users]=user_first_name,user_last_name,user_email_addresses,allow_deletion,user_id,last_synced_at,organization_count' +
'&fields[email_addresses]=email,primary' +
'&fields[designation_accounts]=display_name,organization' +
'&fields[organizations]=name' +
Expand Down
9 changes: 7 additions & 2 deletions src/components/InfiniteList/InfiniteList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,24 +66,24 @@

export interface InfiniteListProps<T, C> {
loading: boolean;
disableHover?: boolean;
caleballdrin marked this conversation as resolved.
Show resolved Hide resolved
EmptyPlaceholder?: ReactElement | null;
ItemOverride?: React.ComponentType<ItemProps> | null;
itemContent: ItemContent<T, C>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
context?: any;
groupBy?: (item: T) => { label: string; order?: number };
disableHover?: boolean;
}

export const InfiniteList = <T, C>({
loading,
disableHover = false,
data = [],
EmptyPlaceholder = null,
ItemOverride = null,
context,
groupBy,
itemContent,
disableHover = false,
...props
}: Omit<GroupedVirtuosoProps<T, C>, 'groupCounts' | 'itemContent'> &
InfiniteListProps<T, C>): ReactElement => {
Expand All @@ -109,6 +109,11 @@
ScrollSeekPlaceholder: SkeletonItem,
...props.components,
},
scrollSeekConfiguration: {
enter: (velocity) => Math.abs(velocity) > 200,
exit: (velocity) => Math.abs(velocity) < 10,

Check warning on line 114 in src/components/InfiniteList/InfiniteList.tsx

View check run for this annotation

Codecov / codecov/patch

src/components/InfiniteList/InfiniteList.tsx#L114

Added line #L114 was not covered by tests
...props.scrollSeekConfiguration,
},
overscan: 2000,
};

Expand Down
Loading
Loading