Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
31 changes: 19 additions & 12 deletions components/dashboard/filters/FilterDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export function SetFilter({ tmpValue, setTmpValue, filterKey, filters, setFilter
valueRenderer={filterConfig.valueRenderer}
labelMsg={filterConfig.labelMsg}
meta={meta}
values={values}
/>
<div className="border-t p-2">
<Button
Expand Down Expand Up @@ -203,7 +204,7 @@ const FilterButton = ({ filterKey, setFilter, filters, tmpValue, open, highlight
};

function FilterDropdown<FV, FM>({
filterKey: currentFilterKey,
filterKey: initialFilterKey,
remainingFilters,
filters,
setFilter,
Expand All @@ -222,28 +223,34 @@ function FilterDropdown<FV, FM>({
locked?: boolean;
}) {
const [open, setOpen] = React.useState(false);
const [filterKey, setFilterKey] = React.useState(currentFilterKey);
const [tmpValue, setTmpValue] = React.useState(values[currentFilterKey]);
const [activeFilterKey, setActiveFilterKey] = React.useState(initialFilterKey);
const currentFilterValue = values[initialFilterKey];
const [tmpValue, setTmpValue] = React.useState(currentFilterValue);

React.useEffect(() => {
setFilterKey(currentFilterKey);
setTmpValue(values[currentFilterKey]);
}, [remainingFilters, currentFilterKey, values]);
// Reset if the currently selected filter type is no longer available
// (e.g., it was just applied and moved to displayed filters)
if (activeFilterKey && remainingFilters && !remainingFilters.includes(activeFilterKey)) {
setActiveFilterKey(initialFilterKey);
}
// Always sync tmpValue with the current filter value
setTmpValue(currentFilterValue);
}, [activeFilterKey, initialFilterKey, currentFilterValue, remainingFilters]);

return (
<Popover
open={open}
onOpenChange={open => {
if (!locked) {
setFilterKey(currentFilterKey);
setTmpValue(values[currentFilterKey]);
setActiveFilterKey(initialFilterKey);
setTmpValue(values[initialFilterKey]);
setOpen(open);
}
}}
>
<PopoverAnchor>
<FilterButton
filterKey={filterKey}
filterKey={activeFilterKey}
filters={filters}
tmpValue={tmpValue}
setFilter={setFilter}
Expand All @@ -254,19 +261,19 @@ function FilterDropdown<FV, FM>({
/>
</PopoverAnchor>
<PopoverContent className="w-[260px] p-0" align="start">
{filterKey ? (
{activeFilterKey ? (
<SetFilter
tmpValue={tmpValue}
setTmpValue={setTmpValue}
filters={filters}
filterKey={filterKey}
filterKey={activeFilterKey}
setFilter={setFilter}
setOpen={setOpen}
meta={meta}
values={values}
/>
) : (
<ChooseFilterType remainingFilters={remainingFilters} filters={filters} setFilterKey={setFilterKey} />
<ChooseFilterType remainingFilters={remainingFilters} filters={filters} setFilterKey={setActiveFilterKey} />
)}
</PopoverContent>
</Popover>
Expand Down
14 changes: 7 additions & 7 deletions components/dashboard/filters/Filterbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ import { Separator } from '../../ui/Separator';
import FilterDropdown from './FilterDropdown';

function useGetFilterbarOptions(filters, values, defaultSchemaValues, meta) {
const filterKeys = Object.keys(filters);
const filterKeys = React.useMemo(() => Object.keys(filters), [filters]);
const [displayedFilters, setDisplayedFilters] = React.useState(
filterKeys.filter(key => filterShouldDisplay(key, { values, filters, defaultSchemaValues, meta })),
);
const remainingFilters = filterKeys.filter(key =>
filterShouldBeInAddFilterOptions(key, { values, filters, defaultSchemaValues, meta }),

const remainingFilters = React.useMemo(
() =>
filterKeys.filter(key => filterShouldBeInAddFilterOptions(key, { values, filters, defaultSchemaValues, meta })),
[filterKeys, values, filters, defaultSchemaValues, meta],
);

// When the values change, this effect makes sure to update the displayed filter keys array and maintain the order of the filters
Expand Down Expand Up @@ -53,6 +56,7 @@ const renderFilter = ({ filters, values, key, activeViewId, views, lockViewFilte
highlighted={highlighted}
intl={intl}
meta={meta}
values={values}
/>
);
} else {
Expand Down Expand Up @@ -155,10 +159,6 @@ export function Filterbar<FV extends Record<string, any>, FM>({
<FilterDropdown
filters={filters}
values={values}
// If last option display it directly
{...(remainingFilters.length === 1 && {
filterKey: remainingFilters[0],
})}
remainingFilters={remainingFilters}
setFilter={setFilter}
meta={meta}
Expand Down
12 changes: 5 additions & 7 deletions components/dashboard/filters/HostedAccountFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,21 @@ const hostedAccountFilterSearchQuery = gql`
${accountHoverCardFields}
`;

export const AccountRenderer = ({
account,
inOptionsList,
}: {
export const AccountRenderer = (props: {
account: Partial<AccountHoverCardFieldsFragment> & {
slug: Account['slug'];
};
inOptionsList?: boolean; // For positioning the HoverCard to the right to prevent blocking options list
}) => {
const { data } = useQuery<AccountFilterQuery>(accountFilterQuery, {
variables: { slug: account.slug },
variables: { slug: props.account.slug },
fetchPolicy: 'cache-first',

// skip query if there is already a field from the hover card data (such as description),
// to prevent fetching all accounts when used in the combo select filter that already queries for these fields
skip: !!account.description && !!account.type,
skip: !!props.account.description && !!props.account.type,
});
const account = data?.account ?? props.account;

const trigger = (
<div className="flex h-full w-full max-w-48 items-center justify-between gap-2 overflow-hidden">
Expand All @@ -61,7 +59,7 @@ export const AccountRenderer = ({
return (
<AccountHoverCard
account={data.account}
{...(inOptionsList && { hoverCardContentProps: { side: 'right', sideOffset: 24 } })}
{...(props.inOptionsList && { hoverCardContentProps: { side: 'right', sideOffset: 24 } })}
trigger={trigger}
/>
);
Expand Down
119 changes: 119 additions & 0 deletions components/dashboard/filters/OrderCreatedByFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import React from 'react';
import { useLazyQuery } from '@apollo/client';
import { defineMessage } from 'react-intl';
import { z } from 'zod';

import type { FilterComponentProps, FilterConfig } from '@/lib/filters/filter-types';
import { isMulti } from '@/lib/filters/schemas';
import { gql } from '@/lib/graphql/helpers';
import type { AccountHoverCardFieldsFragment, HostContext } from '@/lib/graphql/types/v2/graphql';
import type { ExpectedFundsFilter } from '@/lib/graphql/types/v2/schema';

import { accountHoverCardFields } from '../../AccountHoverCard';
import type { FilterValues as OrderFilterValues } from '../sections/contributions/filters';

import ComboSelectFilter from './ComboSelectFilter';
import { AccountRenderer } from './HostedAccountFilter';

const createdByFilterSearchQuery = gql`
query CreatedByFilterSearch(
$account: AccountReferenceInput!
$searchTerm: String
$expectedFundsFilter: ExpectedFundsFilter
$hostContext: HostContext
$status: [OrderStatus]
) {
orders(
account: $account
filter: INCOMING
expectedFundsFilter: $expectedFundsFilter
hostContext: $hostContext
status: $status
) {
createdByUsers(searchTerm: $searchTerm, limit: 20) {
nodes {
id
...AccountHoverCardFields
}
}
}
}
${accountHoverCardFields}
`;

export type OrderCreatedByFilterMeta = {
accountSlug: string;
expectedFundsFilter?: ExpectedFundsFilter;
};

const schema = isMulti(z.string()).optional();

const resultNodeToOption = (account: Partial<AccountHoverCardFieldsFragment>) => ({
label: <AccountRenderer account={account as AccountHoverCardFieldsFragment & { slug: string }} inOptionsList />,
keywords: [account.name],
value: account.slug,
});

type RequiredFilterValueTypes = {
hostContext?: HostContext;
expectedFundsFilter?: ExpectedFundsFilter;
status?: OrderFilterValues['status'];
};

function OrderCreatedByFilter({
meta,
values,
...props
}: FilterComponentProps<z.infer<typeof schema>, OrderCreatedByFilterMeta, RequiredFilterValueTypes>) {
const [options, setOptions] = React.useState<{ label: React.ReactNode; keywords: string[]; value: string }[]>([]);
const [search, { loading, data }] = useLazyQuery(createdByFilterSearchQuery, {
fetchPolicy: 'cache-first',
notifyOnNetworkStatusChange: true,
});

const searchFunc = React.useCallback(
(searchTerm: string) => {
search({
variables: {
account: { slug: meta.accountSlug },
searchTerm: searchTerm || undefined,
hostContext: values.hostContext,
expectedFundsFilter: values.expectedFundsFilter,
status: values.status,
},
});
},
[meta.accountSlug, search, values.hostContext, values.expectedFundsFilter, values.status],
);

// Load initial options on mount
React.useEffect(() => {
search({
variables: {
account: { slug: meta.accountSlug },
searchTerm: undefined,
hostContext: values.hostContext,
expectedFundsFilter: values.expectedFundsFilter,
status: values.status,
},
});
}, [meta.accountSlug, search, values.hostContext, values.expectedFundsFilter, values.status]);

React.useEffect(() => {
if (!loading && data?.orders?.createdByUsers?.nodes) {
setOptions(data.orders.createdByUsers.nodes.map(resultNodeToOption));
}
}, [loading, data]);

return <ComboSelectFilter options={options} loading={loading} searchFunc={searchFunc} isMulti {...props} />;
}

export const orderCreatedByFilter: FilterConfig<z.infer<typeof schema>> = {
schema: schema,
filter: {
labelMsg: defineMessage({ defaultMessage: 'Created by', id: 'Agreement.createdBy' }),
Component: OrderCreatedByFilter,
valueRenderer: ({ value, ...props }) => <AccountRenderer account={{ slug: value }} {...props} />,
},
toVariables: (values, key) => ({ [key]: values.map(slug => ({ slug })) }),
};
2 changes: 1 addition & 1 deletion components/dashboard/sections/HostDashboardAgreements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ type FilterMeta = {
};

const filters: FilterComponentConfigs<z.infer<typeof schema>, FilterMeta> = {
account: hostedAccountFilter.filter,
account: { static: true, ...hostedAccountFilter.filter },
};

const HostDashboardAgreements = ({ accountSlug: hostSlug }: DashboardSectionProps) => {
Expand Down
Loading
Loading