Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b22c6a5
Adds firstDonationDate to PGA
wjames111 Oct 29, 2025
81e5c83
Adds ReportsStaffExpenses to PGA
wjames111 Oct 29, 2025
f2b81be
Add balance card
wjames111 Oct 29, 2025
79782d6
Add cardSkeleton
wjames111 Oct 29, 2025
cf6028f
Opens Contact to donations tab
wjames111 Oct 29, 2025
5586d43
Adds predefined filters
wjames111 Oct 30, 2025
5a3bf98
Adds Print button
wjames111 Oct 30, 2025
26382bc
Typo in readme
wjames111 Oct 30, 2025
c8f9bc7
Removes duplicate FundFields fragment
wjames111 Nov 3, 2025
80da160
Updates BalanceCard and Skeleton
wjames111 Nov 3, 2025
83f8fca
Adds totalDonationSum
wjames111 Nov 6, 2025
3e81832
Add pledge_frequency to commitment amount
wjames111 Nov 6, 2025
76462cb
Update totalDonationSum to donationPeriodTotalSum
wjames111 Nov 6, 2025
4c83cac
Fix cardSkeleton currency format
wjames111 Nov 7, 2025
23a6b9c
Re-use formatBalance in BalanceCard
wjames111 Nov 10, 2025
00adc2f
Fix styles in BalanceCard
wjames111 Nov 10, 2025
62bd533
Updates paginationFieldPolicy for PGA
wjames111 Nov 11, 2025
245efca
Adds handlePrint functionality; Updates pagination; Update loading st…
wjames111 Nov 11, 2025
f9ebeb2
Adds print button
wjames111 Nov 11, 2025
cab27aa
Update PGA tests, add print functionality test
wjames111 Nov 11, 2025
e45b001
Updates PGA URL test with donations param
wjames111 Nov 11, 2025
285d132
Make sortModel uncontrolled
wjames111 Nov 11, 2025
18f9d75
Fix for flickering table
wjames111 Nov 12, 2025
a3163fc
Remove extra loading spinner
wjames111 Nov 13, 2025
012315f
Fix cardSkeleton
wjames111 Nov 14, 2025
e37710b
Updates Reports to use a MenuIcon
wjames111 Nov 6, 2025
7e4b831
Gives FilterButton background style when open
wjames111 Nov 10, 2025
0376e8b
Put getHeaderTitleAccess into a helper function
wjames111 Nov 10, 2025
2eae9be
Fix rebase conflict
wjames111 Jan 14, 2026
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ const handleLoadMore = () => {
};
```

If you want to load all the pages, use the `useFetchAllPages` hook. As long as your operation accepts an `after` variable, this hook will load all the pages into `data`, and set `loading` to `false` once all pages have loaded. For this to work, the field you are querying needs to have its field policy set to `paginationFieldPolicy` in `src/lib/client.ts` so that Apollo will know to merge the results from the additional pages back into the result from the initial query. Consult the [Apollo docs](https://www.apollographql.com/docs/react/caching/cache-field-behavior/) for more information.
If you want to load all the pages, use the `useFetchAllPages` hook. As long as your operation accepts an `after` variable, this hook will load all the pages into `data`, and set `loading` to `false` once all pages have loaded. For this to work, the field you are querying needs to have its field policy set to `paginationFieldPolicy` in `src/lib/apollo/cache.ts` so that Apollo will know to merge the results from the additional pages back into the result from the initial query. Consult the [Apollo docs](https://www.apollographql.com/docs/react/caching/cache-field-behavior/) for more information.

```ts
import { useFetchAllPages } from 'src/hooks/useFetchAllPages';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ describe('partnerCurrency page', () => {

const leftPanel = getByTestId('SidePanelsLayoutLeftPanel');

userEvent.click(getByRole('button', { name: 'Toggle Filter Panel' }));
userEvent.click(getByRole('button', { name: 'Toggle Navigation Panel' }));
expect(leftPanel).toHaveStyle('transform: none');

userEvent.click(await findByTestId('CloseIcon'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,12 @@ describe('partnerGivingAnalysis page', () => {
});

it('changes the URL when a contact is selected', async () => {
const { findByRole } = render(<TestingComponent />);
const { findAllByRole } = render(<TestingComponent />);

expect(push).not.toHaveBeenCalled();

userEvent.click(await findByRole('link', { name: 'John Doe' }));
const links = await findAllByRole('link', { name: 'John Doe' });
userEvent.click(links[0]);

expect(push).toHaveBeenCalled();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { useAccountListId } from 'src/hooks/useAccountListId';
import useGetAppSettings from 'src/hooks/useGetAppSettings';
import { useContactFiltersQuery } from '../../contacts/Contacts.generated';
import { Panel } from '../helpers';
import { preDefinedFilters } from './preDefinedFilters';

// The order here is also the sort order and the display order
const reportFilters = [
Expand Down Expand Up @@ -92,6 +93,7 @@ const PageContent: React.FC = () => {
) : (
<DynamicFilterPanel
filters={filterGroups}
preDefinedFilters={preDefinedFilters}
Comment thread
wjames111 marked this conversation as resolved.
defaultExpandedFilterGroups={new Set(['Report Filters'])}
savedFilters={[]}
onClose={() => setPanelOpen(null)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { DateTime } from 'luxon';
import { UserOptionFragment } from 'src/components/Shared/Filters/FilterPanel.generated';

export const preDefinedFilters: UserOptionFragment[] = [
{
id: 'pre-defined-filter-last-full-12-months',
key: 'saved_contacts_filter_Last_Full_12_Months',
value: JSON.stringify({
donationDate: {
min: DateTime.now().minus({ months: 12 }).startOf('month').toISODate(),
max: DateTime.now().minus({ months: 1 }).endOf('month').toISODate(),
},
}),
__typename: 'Option' as const,
},
{
id: 'pre-defined-filter-last-full-month',
key: 'saved_contacts_filter_Last_Full_Month',
value: JSON.stringify({
donationDate: {
min: DateTime.now().minus({ months: 1 }).startOf('month').toISODate(),
max: DateTime.now().minus({ months: 1 }).endOf('month').toISODate(),
},
}),
__typename: 'Option' as const,
},
{
id: 'pre-defined-filter-month-to-date',
key: 'saved_contacts_filter_Month_to_Date',
value: JSON.stringify({
donationDate: {
min: DateTime.now().startOf('month').toISODate(),
max: DateTime.now().toISODate(),
},
}),
__typename: 'Option' as const,
},
{
id: 'pre-defined-filter-year-to-date',
key: 'saved_contacts_filter_Year_to_Date',
value: JSON.stringify({
donationDate: {
min: DateTime.now().startOf('year').toISODate(),
max: DateTime.now().toISODate(),
},
}),
__typename: 'Option' as const,
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ describe('salaryCurrency page', () => {

const leftPanel = getByTestId('SidePanelsLayoutLeftPanel');

userEvent.click(getByRole('button', { name: 'Toggle Filter Panel' }));
userEvent.click(getByRole('button', { name: 'Toggle Navigation Panel' }));
expect(leftPanel).toHaveStyle('transform: none');

userEvent.click(await findByTestId('CloseIcon'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,8 @@ describe('DesignationAccountsReport', () => {
</ThemeProvider>,
);

expect(getByTestId('ReportsFilterIcon')).toBeInTheDocument();
userEvent.click(getByTestId('ReportsFilterIcon'));
expect(getByTestId('ReportsMenuIcon')).toBeInTheDocument();
userEvent.click(getByTestId('ReportsMenuIcon'));
await waitFor(() => expect(onNavListToggle).toHaveBeenCalled());
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,8 @@ describe('DonationsReport', () => {
onNavListToggle.mockClear();
const { getByTestId } = render(<TestComponent />);

expect(getByTestId('ReportsFilterIcon')).toBeInTheDocument();
userEvent.click(getByTestId('ReportsFilterIcon'));
expect(getByTestId('ReportsMenuIcon')).toBeInTheDocument();
userEvent.click(getByTestId('ReportsMenuIcon'));
await waitFor(() => expect(onNavListToggle).toHaveBeenCalled());
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ describe('ExpectedMonthlyTotalReport', () => {
it('renders nav list icon and onclick triggers onNavListToggle', async () => {
const { getByTestId } = render(<TestComponent />);

expect(getByTestId('ReportsFilterIcon')).toBeInTheDocument();
userEvent.click(getByTestId('ReportsFilterIcon'));
expect(getByTestId('ReportsMenuIcon')).toBeInTheDocument();
userEvent.click(getByTestId('ReportsMenuIcon'));
await waitFor(() => expect(onNavListToggle).toHaveBeenCalled());
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ describe('FinancialAccounts', () => {
<Components mocks={FinancialAccountsMock} />,
);

expect(getByTestId('ReportsFilterIcon')).toBeInTheDocument();
userEvent.click(getByTestId('ReportsFilterIcon'));
expect(getByTestId('ReportsMenuIcon')).toBeInTheDocument();
userEvent.click(getByTestId('ReportsMenuIcon'));
await waitFor(() => expect(onNavListToggle).toHaveBeenCalled());
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useContext } from 'react';
import { Box, Button, Divider } from '@mui/material';
import { styled } from '@mui/material/styles';
import { useTranslation } from 'react-i18next';
import { Panel } from 'pages/accountLists/[accountListId]/reports/helpers';
import { FilterButton } from 'src/components/Shared/Header/styledComponents';
import { NavFilterIcon } from 'src/components/Shared/styledComponents/NavFilterIcon';
import { SearchBox } from 'src/components/common/SearchBox/SearchBox';
Expand Down Expand Up @@ -48,6 +49,7 @@ export const TransactionsHeader: React.FC<TransactionsHeaderProps> = ({
financialAccountId,
handleNavListToggle,
handleFilterListToggle,
panelOpen,
} = useContext(FinancialAccountContext) as FinancialAccountType;

const { searchTerm, setSearchTerm } = useUrlFilters();
Expand All @@ -66,7 +68,10 @@ export const TransactionsHeader: React.FC<TransactionsHeaderProps> = ({
<Divider />
<Header>
<HeaderFilterAction>
<FilterButton onClick={handleFilterListToggle}>
<FilterButton
panelOpen={panelOpen === Panel.Filters}
onClick={handleFilterListToggle}
>
<NavFilterIcon titleAccess={t('Toggle Filter Panel')} />
</FilterButton>
</HeaderFilterAction>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next';
import Loading from 'src/components/Loading/Loading';
import { Notification } from 'src/components/Notification/Notification';
import { EmptyReport } from 'src/components/Reports/EmptyReport/EmptyReport';
import { HeaderTypeEnum } from 'src/components/Shared/MultiPageLayout/MultiPageHeader';
import { FourteenMonthReportHeader as Header } from './Layout/Header/Header';
import {
FourteenMonthReportTable as Table,
Expand Down Expand Up @@ -105,6 +106,7 @@ export const FourteenMonthReport: React.FC<Props> = ({
onNavListToggle={onNavListToggle}
onPrint={handlePrint}
title={title}
headerType={HeaderTypeEnum.Report}
/>
{loading ? (
<Loading />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { ThemeProvider } from '@mui/material/styles';
import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { HeaderTypeEnum } from 'src/components/Shared/MultiPageLayout/MultiPageHeader';
import theme from 'src/theme';
import { FourteenMonthReportCurrencyType } from '../../FourteenMonthReport';
import { FourteenMonthReportHeader } from './Header';
Expand Down Expand Up @@ -63,6 +64,7 @@ describe('FourteenMonthReportHeader', () => {
isExpanded={true}
isMobile={true}
isNavListOpen={true}
headerType={HeaderTypeEnum.Report}
title={title}
onExpandToggle={onExpandToggle}
onNavListToggle={onNavListToggle}
Expand All @@ -72,7 +74,7 @@ describe('FourteenMonthReportHeader', () => {
);

userEvent.click(
getByRole('button', { hidden: true, name: 'Toggle Filter Panel' }),
getByRole('button', { hidden: true, name: 'Toggle Navigation Panel' }),
);
expect(onNavListToggle).toHaveBeenCalled();
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import React, { FC } from 'react';
import FilterList from '@mui/icons-material/FilterList';
import MenuIcon from '@mui/icons-material/Menu';
import { Box, Grid, IconButton, Typography } from '@mui/material';
import { styled } from '@mui/material/styles';
import { useTranslation } from 'react-i18next';
import { HeaderTypeEnum } from 'src/components/Shared/MultiPageLayout/MultiPageHeader';
import { getHeaderTitleAccess } from 'src/components/Shared/MultiPageLayout/helpers';
import theme from 'src/theme';
import { FourteenMonthReportCurrencyType } from '../../FourteenMonthReport';
import { FourteenMonthReportActions } from './Actions/Actions';
Expand All @@ -17,6 +20,7 @@ interface FourteenMonthReportHeaderProps {
onNavListToggle: () => void;
onPrint: (event: React.MouseEvent<unknown>) => void;
title: string;
headerType?: HeaderTypeEnum;
}

const StickyHeader = styled(Box)(({}) => ({
Expand All @@ -41,7 +45,7 @@ const NavListButton = styled(IconButton, {
display: 'inline-block',
width: 48,
height: 48,
borderradius: 24,
borderRadius: 24,
margin: theme.spacing(1),
backgroundColor: panelOpen ? theme.palette.secondary.dark : 'transparent',
'@media print': {
Expand All @@ -55,6 +59,12 @@ const NavListIcon = styled(FilterList)(({ theme }) => ({
color: theme.palette.primary.dark,
}));

const NavMenuIcon = styled(MenuIcon)(({}) => ({
width: 24,
height: 24,
color: theme.palette.primary.dark,
}));

const StyledGrid = styled(Grid)(() => ({
'@media print': {
display: 'none',
Expand All @@ -65,6 +75,7 @@ export const FourteenMonthReportHeader: FC<FourteenMonthReportHeaderProps> = ({
csvData,
currencyType,
title,
headerType,
isExpanded,
isMobile,
isNavListOpen,
Expand All @@ -74,6 +85,11 @@ export const FourteenMonthReportHeader: FC<FourteenMonthReportHeaderProps> = ({
...rest
}) => {
const { t } = useTranslation();
const isReportsHeader = headerType === HeaderTypeEnum.Report;

const titleAccess = headerType
? getHeaderTitleAccess(headerType, t)
: undefined;

return (
<StickyHeader p={2} data-testid="FourteenMonthReportHeader">
Expand All @@ -86,7 +102,11 @@ export const FourteenMonthReportHeader: FC<FourteenMonthReportHeaderProps> = ({
<Grid item>
<Box display="flex" alignItems="center">
<NavListButton panelOpen={isNavListOpen} onClick={onNavListToggle}>
<NavListIcon titleAccess={t('Toggle Filter Panel')} />
{isReportsHeader ? (
<NavMenuIcon titleAccess={titleAccess} />
) : (
<NavListIcon titleAccess={titleAccess} />
)}
</NavListButton>
<HeaderTitle variant="h5">{title}</HeaderTitle>
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ describe('MPGAIncomeExpensesReport', () => {
onNavListToggle.mockClear();
const { getByTestId } = render(<TestComponent />);

expect(getByTestId('ReportsFilterIcon')).toBeInTheDocument();
userEvent.click(getByTestId('ReportsFilterIcon'));
expect(getByTestId('ReportsMenuIcon')).toBeInTheDocument();
userEvent.click(getByTestId('ReportsMenuIcon'));
await waitFor(() => expect(onNavListToggle).toHaveBeenCalled());
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ describe('MPRemindersReport', () => {
onNavListToggle.mockClear();
const { getByTestId } = render(<TestComponent />);

expect(getByTestId('ReportsFilterIcon')).toBeInTheDocument();
userEvent.click(getByTestId('ReportsFilterIcon'));
expect(getByTestId('ReportsMenuIcon')).toBeInTheDocument();
userEvent.click(getByTestId('ReportsMenuIcon'));
await waitFor(() => expect(onNavListToggle).toHaveBeenCalled());
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import PrintIcon from '@mui/icons-material/Print';
import { Button, CircularProgress } from '@mui/material';
import { useTranslation } from 'react-i18next';

interface HeaderActionsProps {
onPrint: () => void;
loading?: boolean;
}

export const HeaderActions: React.FC<HeaderActionsProps> = ({
onPrint,
loading = false,
}) => {
const { t } = useTranslation();

return (
Comment thread
wjames111 marked this conversation as resolved.
<Button
startIcon={loading ? <CircularProgress size={20} /> : <PrintIcon />}
onClick={onPrint}
variant="outlined"
disabled={loading}
>
{loading ? t('Loading...') : t('Print')}
</Button>
);
};
Loading
Loading