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
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { _UIComponentProps, ExternalComponentType, TransactionOverviewComponentProps } from '../../types';
import { _UIComponentProps, ExternalComponentType, TransactionsOverviewComponentProps } from '../../types';
import Transactions from './components/TransactionsOverviewContainer/TransactionsOverviewContainer';
import UIElement from '../UIElement/UIElement';

export class TransactionsElement extends UIElement<TransactionOverviewComponentProps> {
export class TransactionsElement extends UIElement<TransactionsOverviewComponentProps> {
public static type: ExternalComponentType = 'transactions';

constructor(props: _UIComponentProps<TransactionOverviewComponentProps>) {
constructor(props: _UIComponentProps<TransactionsOverviewComponentProps>) {
super(props);
this.componentToRender = this.componentToRender.bind(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,22 @@ import { ITransactionCategory } from '../../../../../types';
import { getTransactionCategory } from '../../../../utils/translation/getters';
import { StructuredListItem } from '../../../../internal/StructuredList/types';
import { TypographyElement, TypographyVariant } from '../../../../internal/Typography/types';
import { TransactionsOverviewInsightsProps } from '../TransactionsOverview/TransactionsOverviewInsights';
import { ErrorMessageDisplay } from '../../../../internal/ErrorMessageDisplay/ErrorMessageDisplay';
import AmountDisplay, { AmountDisplayProps } from '../AmountDisplay/AmountDisplay';
import useCoreContext from '../../../../../core/Context/useCoreContext';
import useTransactionsTotals from '../../hooks/useTransactionsTotals';
import useCurrenciesLookup from '../../hooks/useCurrenciesLookup';
import Typography from '../../../../internal/Typography/Typography';
import StructuredList from '../../../../internal/StructuredList';
import './InsightsTotals.scss';

const InsightsTotals = ({ currency, currenciesLookupResult, transactionsTotalsResult }: TransactionsOverviewInsightsProps) => {
interface InsightsTotalsProps {
currency?: string;
currenciesLookupResult: ReturnType<typeof useCurrenciesLookup>;
transactionsTotalsResult: ReturnType<typeof useTransactionsTotals>;
}

const InsightsTotals = ({ currency, currenciesLookupResult, transactionsTotalsResult }: InsightsTotalsProps) => {
const { i18n } = useCoreContext();
const { currenciesDictionary } = currenciesLookupResult;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ import {
import { compareTransactionsFilters } from '../utils';
import { FilterBar } from '../../../../internal/FilterBar';
import { selectionOptionsFor } from '../MultiSelectionFilter';
import { IBalanceAccountBase } from '../../../../../types';
import { TransactionsFilters as Filters } from '../../types';
import { FilterBarState } from '../../../../internal/FilterBar/types';
import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks';
import { useTransactionsOverviewContext } from '../../context/TransactionsOverviewContext';
import { containerQueries, useResponsiveContainer } from '../../../../../hooks/useResponsiveContainer';
import BalanceAccountSelector from '../../../../internal/FormFields/Select/BalanceAccountSelector';
import useBalanceAccountSelection from '../../../../../hooks/useBalanceAccountSelection';
Expand All @@ -32,26 +31,22 @@ const balanceAccountFilterChangedCallback = (() => {
};
})();

export interface TransactionsFiltersProps extends Omit<FilterBarState, 'setShowingFilters'> {
availableCurrencies: readonly string[];
balanceAccounts?: IBalanceAccountBase[];
isTransactionsView: boolean;
insightsCurrency?: string;
onChange?: (filters: Readonly<Filters>) => void;
setInsightsCurrency?: (currency?: string) => void;
}

const TransactionsFilters = ({
availableCurrencies,
balanceAccounts,
isTransactionsView,
onChange,
insightsCurrency,
setInsightsCurrency,
...filterBarProps
}: TransactionsFiltersProps) => {
const TransactionsFilters = () => {
// prettier-ignore
const {
filterBarState,
isTransactionsView,
balanceAccounts,
currenciesLookupResult,
insightsCurrency,
onFiltersChange,
setInsightsCurrency,
} = useTransactionsOverviewContext();

const { i18n } = useCoreContext();
const { setShowingFilters, ...filterBarProps } = filterBarState;

const availableCurrencies = currenciesLookupResult.sortedCurrencies;
const eventSubCategory = isTransactionsView ? TRANSACTION_ANALYTICS_SUBCATEGORY_LIST : TRANSACTION_ANALYTICS_SUBCATEGORY_INSIGHTS;
const initialFiltersRef = useRef<Filters>({ ...INITIAL_FILTERS });
const isSmContainer = useResponsiveContainer(containerQueries.down.xs);
Expand Down Expand Up @@ -122,9 +117,9 @@ const TransactionsFilters = ({
useEffect(() => {
if (cachedCurrentFiltersRef.current !== currentFilters) {
cachedCurrentFiltersRef.current = currentFilters;
onChange?.(currentFilters);
onFiltersChange?.(currentFilters);
}
}, [onChange, currentFilters]);
}, [onFiltersChange, currentFilters]);

useEffect(() => {
if (!initialFiltersRef.current.balanceAccount && balanceAccount) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { containerQueries, useResponsiveContainer } from '../../../../../hooks/u
import { PopoverContainerPosition, PopoverContainerVariant } from '../../../../internal/Popover/types';
import { TypographyElement, TypographyVariant } from '../../../../internal/Typography/types';
import { downloadBlob, EMPTY_ARRAY, isFunction, uniqueId } from '../../../../../utils';
import { useTransactionsOverviewContext } from '../../context/TransactionsOverviewContext';
import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks';
import { useConfigContext } from '../../../../../core/ConfigContext';
import { AlertTypeOption } from '../../../../internal/Alert/types';
Expand All @@ -26,7 +27,6 @@ import { fixedForwardRef } from '../../../../../utils/preact';
import { getTransactionsFilterQueryParams } from '../utils';
import { TranslationKey } from '../../../../../translations';
import { Tag } from '../../../../internal/Tag/Tag';
import { TransactionsFilters } from '../../types';
import { PropsWithChildren } from 'preact/compat';
import { classes } from './constants';
import './TransactionsExport.scss';
Expand Down Expand Up @@ -68,8 +68,10 @@ const SectionTitle = ({ children, ...textProps }: PropsWithChildren<{ id?: strin
</Text>
);

const TransactionsExport = ({ disabled, filters, now }: { disabled?: boolean; filters: Readonly<TransactionsFilters>; now: number }) => {
const TransactionsExport = ({ disabled }: { disabled?: boolean }) => {
const { filters, lastFiltersChangeTimestamp } = useTransactionsOverviewContext();
const { i18n } = useCoreContext();

const userEvents = useAnalyticsContext();
const isSmContainer = useResponsiveContainer(containerQueries.down.xs);

Expand All @@ -91,12 +93,12 @@ const TransactionsExport = ({ disabled, filters, now }: { disabled?: boolean; fi
] as const;

const exportParams = {
...getTransactionsFilterQueryParams(filters, now),
...getTransactionsFilterQueryParams(filters, lastFiltersChangeTimestamp),
sortDirection: 'desc' as const,
};

return [activeFilters, exportParams];
}, [filters, now]);
}, [filters, lastFiltersChangeTimestamp]);

const { downloadTransactions } = useConfigContext().endpoints;
const canDownloadTransactions = isFunction(downloadTransactions);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { IBalanceAccountBase } from '../../../../../types';
import { ExternalUIComponentProps, TransactionOverviewComponentProps } from '../../../../types';
import { ExternalUIComponentProps, TransactionsOverviewComponentProps } from '../../../../types';
import useTransactionsList from '../../hooks/useTransactionsList';
import useCurrenciesLookup from '../../hooks/useCurrenciesLookup';

type PropsFromTransactionComponent = Pick<
ExternalUIComponentProps<TransactionOverviewComponentProps>,
ExternalUIComponentProps<TransactionsOverviewComponentProps>,
'dataCustomization' | 'onContactSupport' | 'onRecordSelection' | 'showDetails'
>;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,161 +1,22 @@
import cx from 'classnames';
import useTransactionsList from '../../hooks/useTransactionsList';
import useTransactionsViewSwitcher from '../../hooks/useTransactionsViewSwitcher';
import useTransactionsTotals, { GetQueryParams } from '../../hooks/useTransactionsTotals';
import useAccountBalances from '../../../../../hooks/useAccountBalances';
import useCoreContext from '../../../../../core/Context/useCoreContext';
import useCurrenciesLookup from '../../hooks/useCurrenciesLookup';
import TransactionsOverviewList from './TransactionsOverviewList';
import TransactionsOverviewShell from './TransactionsOverviewShell';
import TransactionsOverviewInsights from './TransactionsOverviewInsights';
import TransactionsFilters from '../TransactionFilters/TransactionFilters';
import TransactionsExport from '../TransactionsExport/TransactionsExport';
import SegmentedControl from '../../../../internal/SegmentedControl/SegmentedControl';
import { FilterBarMobileSwitch, useFilterBarState } from '../../../../internal/FilterBar';
import { TransactionOverviewProps, TransactionsFilters as Filters, TransactionsView } from '../../types';
import { useCallback, useMemo, useState } from 'preact/hooks';
import { classes, INITIAL_FILTERS } from '../../constants';
import { Header } from '../../../../internal/Header';
import './TransactionsOverview.scss';

const INSIGHTS_FILTERS_SET = new Set<keyof Filters>(['balanceAccount', 'createdDate']);
const getInsightsTotalsQueryParams: GetQueryParams = ({ balanceAccountId, createdSince, createdUntil }) => ({
balanceAccountId,
createdSince,
createdUntil,
});
const getTransactionsTotalsQueryParams: GetQueryParams = allQueryParams => allQueryParams;

export const TransactionsOverview = ({
onFiltersChanged,
balanceAccounts,
allowLimitSelection,
preferredLimit,
onRecordSelection,
showDetails,
isLoadingBalanceAccount,
onContactSupport,
hideTitle,
dataCustomization,
}: TransactionOverviewProps) => {
const [filters, setFilters] = useState(INITIAL_FILTERS);
const [lastFiltersChangeTimestamp, setLastFiltersChangeTimestamp] = useState(() => Date.now());
const [insightsCurrency, setInsightsCurrency] = useState<string>();

const filterBarState = useFilterBarState();

const { balanceAccount } = filters;
const { isMobileContainer } = filterBarState;
const { i18n } = useCoreContext();

const { activeView, onViewChange, viewTabs } = useTransactionsViewSwitcher();
const isTransactionsView = activeView !== TransactionsView.INSIGHTS;
const hasActiveBalanceAccount = !!balanceAccount?.id;

const onFiltersChange = useCallback((filters: Readonly<Filters>) => {
setLastFiltersChangeTimestamp(Date.now());
setFilters(filters);
}, []);

const accountBalancesResult = useAccountBalances({ balanceAccount });

const insightsTotalsResult = useTransactionsTotals({
fetchEnabled: !isTransactionsView && hasActiveBalanceAccount,
getQueryParams: getInsightsTotalsQueryParams,
applicableFilters: INSIGHTS_FILTERS_SET,
now: lastFiltersChangeTimestamp,
filters,
});

const transactionsTotalsResult = useTransactionsTotals({
fetchEnabled: isTransactionsView && hasActiveBalanceAccount,
getQueryParams: getTransactionsTotalsQueryParams,
now: lastFiltersChangeTimestamp,
filters,
});

const transactionsListResult = useTransactionsList({
fetchEnabled: hasActiveBalanceAccount,
now: lastFiltersChangeTimestamp,
allowLimitSelection,
dataCustomization,
onFiltersChanged,
preferredLimit,
filters,
});

const currenciesLookupResult = useCurrenciesLookup({
defaultCurrency: balanceAccount?.defaultCurrencyCode,
balances: accountBalancesResult.balances,
totals: (isTransactionsView ? transactionsTotalsResult : insightsTotalsResult).totals,
});

const exportButton = useMemo(
() =>
isTransactionsView ? (
<TransactionsExport disabled={!transactionsListResult.page} filters={filters} now={lastFiltersChangeTimestamp} />
) : null,
[filters, isTransactionsView, lastFiltersChangeTimestamp, transactionsListResult.page]
);

const viewSwitcher = useMemo(
() =>
viewTabs.length > 1 ? (
<SegmentedControl
aria-label={i18n.get('transactions.overview.viewSelect.a11y.label')}
activeItem={activeView}
items={viewTabs}
onChange={onViewChange}
/>
) : null,
[activeView, onViewChange, viewTabs, i18n]
);
import { TransactionsOverviewProvider } from '../../context/TransactionsOverviewContext';
import { useTransactionsOverviewContext } from '../../context/TransactionsOverviewContext';
import { TransactionsOverviewProps } from '../../types';

const TransactionsOverviewContent = () => {
const { isTransactionsView } = useTransactionsOverviewContext();
// prettier-ignore
return (
<div className={cx(classes.root, { [classes.rootSmall]: isMobileContainer })}>
<Header hideTitle={hideTitle} titleKey="transactions.overview.title">
<div className={cx({ [classes.filterBarSmall]: isMobileContainer })}>
{isMobileContainer && exportButton}
<FilterBarMobileSwitch {...filterBarState} />
{!isMobileContainer && <>{viewSwitcher}</>}
</div>
</Header>

{isMobileContainer && <>{viewSwitcher}</>}

<div role="toolbar" className={classes.toolbar}>
<TransactionsFilters
{...filterBarState}
availableCurrencies={currenciesLookupResult.sortedCurrencies}
balanceAccounts={balanceAccounts}
isTransactionsView={isTransactionsView}
insightsCurrency={insightsCurrency}
setInsightsCurrency={setInsightsCurrency}
onChange={onFiltersChange}
/>
{!isMobileContainer && <>{exportButton}</>}
</div>

{isTransactionsView ? (
<TransactionsOverviewList
accountBalancesResult={accountBalancesResult}
balanceAccount={balanceAccount}
balanceAccounts={balanceAccounts}
currenciesLookupResult={currenciesLookupResult}
dataCustomization={dataCustomization}
isLoadingBalanceAccount={isLoadingBalanceAccount}
onContactSupport={onContactSupport}
onRecordSelection={onRecordSelection}
showDetails={showDetails}
transactionsListResult={transactionsListResult}
transactionsTotalsResult={transactionsTotalsResult}
/>
) : (
<TransactionsOverviewInsights
currency={insightsCurrency}
currenciesLookupResult={currenciesLookupResult}
transactionsTotalsResult={insightsTotalsResult}
/>
)}
</div>
<TransactionsOverviewShell>
{isTransactionsView ? <TransactionsOverviewList /> : <TransactionsOverviewInsights />}
</TransactionsOverviewShell>
);
};

export const TransactionsOverview = (props: TransactionsOverviewProps) => (
<TransactionsOverviewProvider {...props}>
<TransactionsOverviewContent />
</TransactionsOverviewProvider>
);
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
import { TRANSACTION_ANALYTICS_CATEGORY, TRANSACTION_ANALYTICS_SUBCATEGORY_INSIGHTS } from '../../constants';
import { useDurationEvent } from '../../../../../hooks/useAnalytics/useDurationEvent';
import { useLandedPageEvent } from '../../../../../hooks/useAnalytics/useLandedPageEvent';
import useTransactionsTotals from '../../hooks/useTransactionsTotals';
import useCurrenciesLookup from '../../hooks/useCurrenciesLookup';
import { useTransactionsOverviewContext } from '../../context/TransactionsOverviewContext';
import InsightsTotals from '../InsightsTotals/InsightsTotals';

const sharedAnalyticsEventProperties = {
category: TRANSACTION_ANALYTICS_CATEGORY,
subCategory: TRANSACTION_ANALYTICS_SUBCATEGORY_INSIGHTS,
} as const;

export interface TransactionsOverviewInsightsProps {
currency?: string;
currenciesLookupResult: ReturnType<typeof useCurrenciesLookup>;
transactionsTotalsResult: ReturnType<typeof useTransactionsTotals>;
}
const TransactionsOverviewInsights = () => {
const { currenciesLookupResult, insightsCurrency, insightsTotalsResult } = useTransactionsOverviewContext();

const TransactionsOverviewInsights = (props: TransactionsOverviewInsightsProps) => {
useLandedPageEvent(sharedAnalyticsEventProperties);
useDurationEvent(sharedAnalyticsEventProperties);
return <InsightsTotals {...props} />;

return (
<InsightsTotals currency={insightsCurrency} currenciesLookupResult={currenciesLookupResult} transactionsTotalsResult={insightsTotalsResult} />
);
};

export default TransactionsOverviewInsights;
Loading
Loading