mirror of
https://github.com/actualbudget/actual.git
synced 2026-04-29 02:54:09 -05:00
Separate useSchedules and useSchedulesStatus because not all callers of useSchedules use the status. This saves us some unnecessary queries.
This commit is contained in:
@@ -131,9 +131,7 @@ export function ManageRules({
|
||||
const [filter, setFilter] = useState('');
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const {
|
||||
data: { schedules },
|
||||
} = useSchedules({
|
||||
const { data: schedules = [] } = useSchedules({
|
||||
query: q('schedules').select('*'),
|
||||
});
|
||||
const { data: { list: categories } = { list: [] } } = useCategories();
|
||||
|
||||
@@ -58,7 +58,6 @@ import { useDateFormat } from '@desktop-client/hooks/useDateFormat';
|
||||
import { useFailedAccounts } from '@desktop-client/hooks/useFailedAccounts';
|
||||
import { useLocalPref } from '@desktop-client/hooks/useLocalPref';
|
||||
import { usePayees } from '@desktop-client/hooks/usePayees';
|
||||
import { getSchedulesQuery } from '@desktop-client/hooks/useSchedules';
|
||||
import { SelectedProviderWithItems } from '@desktop-client/hooks/useSelected';
|
||||
import type { Actions } from '@desktop-client/hooks/useSelected';
|
||||
import {
|
||||
@@ -82,6 +81,7 @@ import { pagedQuery } from '@desktop-client/queries/pagedQuery';
|
||||
import type { PagedQuery } from '@desktop-client/queries/pagedQuery';
|
||||
import { useDispatch, useSelector } from '@desktop-client/redux';
|
||||
import type { AppDispatch } from '@desktop-client/redux/store';
|
||||
import { schedulesViewQuery } from '@desktop-client/schedules';
|
||||
import { updateNewTransactions } from '@desktop-client/transactions/transactionsSlice';
|
||||
|
||||
type ConditionEntity = Partial<RuleConditionEntity> | TransactionFilterEntity;
|
||||
@@ -1993,7 +1993,7 @@ export function Account() {
|
||||
const savedFiters = useTransactionFilters();
|
||||
|
||||
const schedulesQuery = useMemo(
|
||||
() => getSchedulesQuery(params.id),
|
||||
() => schedulesViewQuery(params.id),
|
||||
[params.id],
|
||||
);
|
||||
|
||||
|
||||
@@ -94,10 +94,7 @@ function SelectedBalance({ selectedItems, account }: SelectedBalanceProps) {
|
||||
|
||||
let scheduleBalance = 0;
|
||||
|
||||
const {
|
||||
data: { schedules },
|
||||
isFetching,
|
||||
} = useCachedSchedules();
|
||||
const { data: schedules = [], isFetching } = useCachedSchedules();
|
||||
|
||||
if (isFetching) {
|
||||
return null;
|
||||
|
||||
@@ -14,7 +14,6 @@ import { useAccountPreviewTransactions } from '@desktop-client/hooks/useAccountP
|
||||
import { SchedulesProvider } from '@desktop-client/hooks/useCachedSchedules';
|
||||
import { useDateFormat } from '@desktop-client/hooks/useDateFormat';
|
||||
import { useNavigate } from '@desktop-client/hooks/useNavigate';
|
||||
import { getSchedulesQuery } from '@desktop-client/hooks/useSchedules';
|
||||
import { useSheetValue } from '@desktop-client/hooks/useSheetValue';
|
||||
import { useSyncedPref } from '@desktop-client/hooks/useSyncedPref';
|
||||
import {
|
||||
@@ -25,6 +24,7 @@ import { useTransactionsSearch } from '@desktop-client/hooks/useTransactionsSear
|
||||
import { collapseModals, pushModal } from '@desktop-client/modals/modalsSlice';
|
||||
import * as queries from '@desktop-client/queries';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
import { schedulesViewQuery } from '@desktop-client/schedules';
|
||||
import * as bindings from '@desktop-client/spreadsheet/bindings';
|
||||
|
||||
export function AccountTransactions({
|
||||
@@ -33,7 +33,7 @@ export function AccountTransactions({
|
||||
readonly account: AccountEntity;
|
||||
}) {
|
||||
const schedulesQuery = useMemo(
|
||||
() => getSchedulesQuery(account.id),
|
||||
() => schedulesViewQuery(account.id),
|
||||
[account.id],
|
||||
);
|
||||
|
||||
|
||||
@@ -11,16 +11,16 @@ import { SchedulesProvider } from '@desktop-client/hooks/useCachedSchedules';
|
||||
import { useDateFormat } from '@desktop-client/hooks/useDateFormat';
|
||||
import { useNavigate } from '@desktop-client/hooks/useNavigate';
|
||||
import { usePreviewTransactions } from '@desktop-client/hooks/usePreviewTransactions';
|
||||
import { getSchedulesQuery } from '@desktop-client/hooks/useSchedules';
|
||||
import { useTransactions } from '@desktop-client/hooks/useTransactions';
|
||||
import { useTransactionsSearch } from '@desktop-client/hooks/useTransactionsSearch';
|
||||
import { collapseModals, pushModal } from '@desktop-client/modals/modalsSlice';
|
||||
import * as queries from '@desktop-client/queries';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
import { schedulesViewQuery } from '@desktop-client/schedules';
|
||||
import * as bindings from '@desktop-client/spreadsheet/bindings';
|
||||
|
||||
export function AllAccountTransactions() {
|
||||
const schedulesQuery = useMemo(() => getSchedulesQuery(), []);
|
||||
const schedulesQuery = useMemo(() => schedulesViewQuery(), []);
|
||||
|
||||
return (
|
||||
<SchedulesProvider query={schedulesQuery}>
|
||||
|
||||
@@ -12,16 +12,16 @@ import { useDateFormat } from '@desktop-client/hooks/useDateFormat';
|
||||
import { useNavigate } from '@desktop-client/hooks/useNavigate';
|
||||
import { useOffBudgetAccounts } from '@desktop-client/hooks/useOffBudgetAccounts';
|
||||
import { usePreviewTransactions } from '@desktop-client/hooks/usePreviewTransactions';
|
||||
import { getSchedulesQuery } from '@desktop-client/hooks/useSchedules';
|
||||
import { useTransactions } from '@desktop-client/hooks/useTransactions';
|
||||
import { useTransactionsSearch } from '@desktop-client/hooks/useTransactionsSearch';
|
||||
import { collapseModals, pushModal } from '@desktop-client/modals/modalsSlice';
|
||||
import * as queries from '@desktop-client/queries';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
import { schedulesViewQuery } from '@desktop-client/schedules';
|
||||
import * as bindings from '@desktop-client/spreadsheet/bindings';
|
||||
|
||||
export function OffBudgetAccountTransactions() {
|
||||
const schedulesQuery = useMemo(() => getSchedulesQuery('offbudget'), []);
|
||||
const schedulesQuery = useMemo(() => schedulesViewQuery('offbudget'), []);
|
||||
|
||||
return (
|
||||
<SchedulesProvider query={schedulesQuery}>
|
||||
|
||||
@@ -12,16 +12,16 @@ import { useDateFormat } from '@desktop-client/hooks/useDateFormat';
|
||||
import { useNavigate } from '@desktop-client/hooks/useNavigate';
|
||||
import { useOnBudgetAccounts } from '@desktop-client/hooks/useOnBudgetAccounts';
|
||||
import { usePreviewTransactions } from '@desktop-client/hooks/usePreviewTransactions';
|
||||
import { getSchedulesQuery } from '@desktop-client/hooks/useSchedules';
|
||||
import { useTransactions } from '@desktop-client/hooks/useTransactions';
|
||||
import { useTransactionsSearch } from '@desktop-client/hooks/useTransactionsSearch';
|
||||
import { collapseModals, pushModal } from '@desktop-client/modals/modalsSlice';
|
||||
import * as queries from '@desktop-client/queries';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
import { schedulesViewQuery } from '@desktop-client/schedules';
|
||||
import * as bindings from '@desktop-client/spreadsheet/bindings';
|
||||
|
||||
export function OnBudgetAccountTransactions() {
|
||||
const schedulesQuery = useMemo(() => getSchedulesQuery('onbudget'), []);
|
||||
const schedulesQuery = useMemo(() => schedulesViewQuery('onbudget'), []);
|
||||
|
||||
return (
|
||||
<SchedulesProvider query={schedulesQuery}>
|
||||
|
||||
@@ -31,10 +31,7 @@ export function MobileRuleEditPage() {
|
||||
const [rule, setRule] = useState<RuleEntity | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const {
|
||||
data: { schedules },
|
||||
isSuccess,
|
||||
} = useSchedules({
|
||||
const { data: schedules = [], isSuccess } = useSchedules({
|
||||
query: rule?.id
|
||||
? q('schedules').filter({ rule: rule.id, completed: false }).select('*')
|
||||
: q('schedules').filter({ id: null }).select('*'), // Return empty result when no rule,
|
||||
|
||||
@@ -37,9 +37,7 @@ export function MobileRulesPage() {
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [filter, setFilter] = useState('');
|
||||
|
||||
const {
|
||||
data: { schedules },
|
||||
} = useSchedules({
|
||||
const { data: schedules = [] } = useSchedules({
|
||||
query: q('schedules').select('*'),
|
||||
});
|
||||
const { data: { list: categories } = { list: [] } } = useCategories();
|
||||
|
||||
@@ -23,7 +23,10 @@ import { useDateFormat } from '@desktop-client/hooks/useDateFormat';
|
||||
import { useFormat } from '@desktop-client/hooks/useFormat';
|
||||
import { useNavigate } from '@desktop-client/hooks/useNavigate';
|
||||
import { usePayees } from '@desktop-client/hooks/usePayees';
|
||||
import { useSchedules } from '@desktop-client/hooks/useSchedules';
|
||||
import {
|
||||
useSchedules,
|
||||
useScheduleStatus,
|
||||
} from '@desktop-client/hooks/useSchedules';
|
||||
import { useUndo } from '@desktop-client/hooks/useUndo';
|
||||
import { addNotification } from '@desktop-client/notifications/notificationsSlice';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
@@ -38,10 +41,12 @@ export function MobileSchedulesPage() {
|
||||
const format = useFormat();
|
||||
const dateFormat = useDateFormat() || 'MM/dd/yyyy';
|
||||
|
||||
const { isFetching: isSchedulesLoading, data: schedules = [] } = useSchedules(
|
||||
{ query: q('schedules').select('*') },
|
||||
);
|
||||
const {
|
||||
isFetching: isSchedulesLoading,
|
||||
data: { schedules, scheduleStatusMap },
|
||||
} = useSchedules({ query: q('schedules').select('*') });
|
||||
data: { statusLookup = {} },
|
||||
} = useScheduleStatus({ schedules });
|
||||
|
||||
const payees = usePayees();
|
||||
const accounts = useAccounts();
|
||||
@@ -67,7 +72,7 @@ export function MobileSchedulesPage() {
|
||||
const dateStr = schedule.next_date
|
||||
? monthUtilFormat(schedule.next_date, dateFormat)
|
||||
: null;
|
||||
const statusLabel = scheduleStatusMap.get(schedule.id);
|
||||
const statusLabel = statusLookup[schedule.id];
|
||||
|
||||
return (
|
||||
filterIncludes(schedule.name) ||
|
||||
@@ -156,7 +161,7 @@ export function MobileSchedulesPage() {
|
||||
<SchedulesList
|
||||
schedules={filteredSchedules}
|
||||
isLoading={isSchedulesLoading}
|
||||
scheduleStatusMap={scheduleStatusMap}
|
||||
statusLookup={statusLookup}
|
||||
onSchedulePress={handleSchedulePress}
|
||||
onScheduleDelete={handleScheduleDelete}
|
||||
hasCompletedSchedules={hasCompletedSchedules}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Text } from '@actual-app/components/text';
|
||||
import { theme } from '@actual-app/components/theme';
|
||||
import { View } from '@actual-app/components/view';
|
||||
|
||||
import type { ScheduleStatusMap } from 'loot-core/shared/schedules';
|
||||
import type { ScheduleStatusLookup } from 'loot-core/shared/schedules';
|
||||
import type { ScheduleEntity } from 'loot-core/types/models';
|
||||
|
||||
import { SchedulesListItem } from './SchedulesListItem';
|
||||
@@ -20,7 +20,7 @@ type SchedulesListEntry = ScheduleEntity | CompletedSchedulesItem;
|
||||
type SchedulesListProps = {
|
||||
schedules: readonly ScheduleEntity[];
|
||||
isLoading: boolean;
|
||||
scheduleStatusMap: ScheduleStatusMap;
|
||||
statusLookup: ScheduleStatusLookup;
|
||||
onSchedulePress: (schedule: ScheduleEntity) => void;
|
||||
onScheduleDelete: (schedule: ScheduleEntity) => void;
|
||||
hasCompletedSchedules?: boolean;
|
||||
@@ -31,7 +31,7 @@ type SchedulesListProps = {
|
||||
export function SchedulesList({
|
||||
schedules,
|
||||
isLoading,
|
||||
scheduleStatusMap,
|
||||
statusLookup,
|
||||
onSchedulePress,
|
||||
onScheduleDelete,
|
||||
hasCompletedSchedules = false,
|
||||
@@ -125,7 +125,7 @@ export function SchedulesList({
|
||||
) : (
|
||||
<SchedulesListItem
|
||||
value={item}
|
||||
status={scheduleStatusMap.get(item.id) || 'scheduled'}
|
||||
status={statusLookup[item.id] || 'scheduled'}
|
||||
onAction={() => onSchedulePress(item)}
|
||||
onDelete={() => onScheduleDelete(item)}
|
||||
/>
|
||||
|
||||
@@ -315,10 +315,8 @@ type PayeeIconsProps = {
|
||||
|
||||
function PayeeIcons({ transaction, transferAccount }: PayeeIconsProps) {
|
||||
const { id, schedule: scheduleId } = transaction;
|
||||
const {
|
||||
isFetching: isSchedulesLoading,
|
||||
data: { schedules },
|
||||
} = useCachedSchedules();
|
||||
const { isFetching: isSchedulesLoading, data: schedules = [] } =
|
||||
useCachedSchedules();
|
||||
const isPreview = isPreviewId(id);
|
||||
const schedule = schedules.find(s => s.id === scheduleId);
|
||||
const isScheduleRecurring =
|
||||
|
||||
@@ -189,9 +189,7 @@ export function BudgetAutomationsModal({ categoryId }: { categoryId: string }) {
|
||||
onLoaded: setAutomations,
|
||||
});
|
||||
|
||||
const {
|
||||
data: { schedules },
|
||||
} = useSchedules({
|
||||
const { data: schedules = [] } = useSchedules({
|
||||
query: q('schedules').select('*'),
|
||||
});
|
||||
|
||||
|
||||
@@ -44,12 +44,11 @@ export function ScheduledTransactionMenuModal({
|
||||
borderTop: `1px solid ${theme.pillBorder}`,
|
||||
};
|
||||
const scheduleId = transactionId?.split('/')?.[1];
|
||||
const {
|
||||
isFetching: isSchedulesLoading,
|
||||
data: { schedules },
|
||||
} = useSchedules({
|
||||
query: q('schedules').filter({ id: scheduleId }).select('*'),
|
||||
});
|
||||
const { isFetching: isSchedulesLoading, data: schedules = [] } = useSchedules(
|
||||
{
|
||||
query: q('schedules').filter({ id: scheduleId }).select('*'),
|
||||
},
|
||||
);
|
||||
|
||||
if (isSchedulesLoading) {
|
||||
return null;
|
||||
|
||||
@@ -58,7 +58,10 @@ import { GenericInput } from '@desktop-client/components/util/GenericInput';
|
||||
import { useDateFormat } from '@desktop-client/hooks/useDateFormat';
|
||||
import { useFeatureFlag } from '@desktop-client/hooks/useFeatureFlag';
|
||||
import { useFormat } from '@desktop-client/hooks/useFormat';
|
||||
import { useSchedules } from '@desktop-client/hooks/useSchedules';
|
||||
import {
|
||||
useSchedules,
|
||||
useScheduleStatus,
|
||||
} from '@desktop-client/hooks/useSchedules';
|
||||
import {
|
||||
SelectedProvider,
|
||||
useSelected,
|
||||
@@ -366,10 +369,13 @@ function ScheduleDescription({ id }) {
|
||||
const { isNarrowWidth } = useResponsive();
|
||||
const dateFormat = useDateFormat() || 'MM/dd/yyyy';
|
||||
const format = useFormat();
|
||||
const { data: schedules = [], isFetching: isSchedulesLoading } = useSchedules(
|
||||
{ query: q('schedules').filter({ id }).select('*') },
|
||||
);
|
||||
|
||||
const {
|
||||
data: { schedules, scheduleStatusLabelMap: statusLabels } = {},
|
||||
isFetching: isSchedulesLoading,
|
||||
} = useSchedules({ query: q('schedules').filter({ id }).select('*') });
|
||||
data: { statusLabelLookup = {} },
|
||||
} = useScheduleStatus({ schedules });
|
||||
|
||||
if (isSchedulesLoading) {
|
||||
return null;
|
||||
@@ -381,7 +387,7 @@ function ScheduleDescription({ id }) {
|
||||
return <View style={{ flex: 1 }}>{id}</View>;
|
||||
}
|
||||
|
||||
const status = statusLabels.get(schedule.id) as ScheduleStatusLabel;
|
||||
const status = statusLabelLookup[schedule.id] as ScheduleStatusLabel;
|
||||
|
||||
return (
|
||||
<View
|
||||
|
||||
@@ -22,10 +22,7 @@ export function ScheduleValue({ value }: ScheduleValueProps) {
|
||||
const { t } = useTranslation();
|
||||
const payees = usePayees();
|
||||
const byId = getPayeesById(payees);
|
||||
const {
|
||||
data: { schedules },
|
||||
isFetching,
|
||||
} = useSchedules({
|
||||
const { data: schedules = [], isFetching } = useSchedules({
|
||||
query: q('schedules').select('*'),
|
||||
});
|
||||
|
||||
|
||||
@@ -20,7 +20,10 @@ import {
|
||||
ModalHeader,
|
||||
} from '@desktop-client/components/common/Modal';
|
||||
import { Search } from '@desktop-client/components/common/Search';
|
||||
import { useSchedules } from '@desktop-client/hooks/useSchedules';
|
||||
import {
|
||||
useSchedules,
|
||||
useScheduleStatus,
|
||||
} from '@desktop-client/hooks/useSchedules';
|
||||
import { pushModal } from '@desktop-client/modals/modalsSlice';
|
||||
import type { Modal as ModalType } from '@desktop-client/modals/modalsSlice';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
@@ -40,12 +43,15 @@ export function ScheduleLink({
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const [filter, setFilter] = useState(accountName || '');
|
||||
const { isFetching: isSchedulesLoading, data: schedules = [] } = useSchedules(
|
||||
{
|
||||
query: q('schedules').filter({ completed: false }).select('*'),
|
||||
},
|
||||
);
|
||||
|
||||
const {
|
||||
isFetching: isSchedulesLoading,
|
||||
data: { schedules, scheduleStatusMap },
|
||||
} = useSchedules({
|
||||
query: q('schedules').filter({ completed: false }).select('*'),
|
||||
});
|
||||
data: { statusLookup = {} },
|
||||
} = useScheduleStatus({ schedules });
|
||||
|
||||
const searchInput = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
@@ -150,7 +156,7 @@ export function ScheduleLink({
|
||||
close();
|
||||
}}
|
||||
schedules={schedules}
|
||||
scheduleStatusMap={scheduleStatusMap}
|
||||
statusLookup={statusLookup}
|
||||
style={null}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -18,7 +18,7 @@ import { getNormalisedString } from 'loot-core/shared/normalisation';
|
||||
import { getScheduledAmount } from 'loot-core/shared/schedules';
|
||||
import type {
|
||||
ScheduleStatus,
|
||||
ScheduleStatusMap,
|
||||
ScheduleStatusLookup,
|
||||
} from 'loot-core/shared/schedules';
|
||||
import type { ScheduleEntity } from 'loot-core/types/models';
|
||||
|
||||
@@ -43,7 +43,7 @@ import { usePayees } from '@desktop-client/hooks/usePayees';
|
||||
type SchedulesTableProps = {
|
||||
isLoading?: boolean;
|
||||
schedules: readonly ScheduleEntity[];
|
||||
scheduleStatusMap: ScheduleStatusMap;
|
||||
statusLookup: ScheduleStatusLookup;
|
||||
filter: string;
|
||||
allowCompleted: boolean;
|
||||
onSelect: (schedule: ScheduleEntity) => void;
|
||||
@@ -204,11 +204,11 @@ function ScheduleRow({
|
||||
onAction,
|
||||
onSelect,
|
||||
minimal,
|
||||
scheduleStatusMap,
|
||||
statusLookup,
|
||||
dateFormat,
|
||||
}: {
|
||||
schedule: ScheduleEntity;
|
||||
scheduleStatusMap: ScheduleStatusMap;
|
||||
statusLookup: ScheduleStatusLookup;
|
||||
dateFormat: string;
|
||||
} & Pick<SchedulesTableProps, 'onSelect' | 'onAction' | 'minimal'>) {
|
||||
const { t } = useTranslation();
|
||||
@@ -250,7 +250,7 @@ function ScheduleRow({
|
||||
>
|
||||
<OverflowMenu
|
||||
schedule={schedule}
|
||||
status={scheduleStatusMap.get(schedule.id)}
|
||||
status={statusLookup[schedule.id]}
|
||||
onAction={(action, id) => {
|
||||
onAction(action, id);
|
||||
resetPosition();
|
||||
@@ -283,7 +283,7 @@ function ScheduleRow({
|
||||
: null}
|
||||
</Field>
|
||||
<Field width={120} name="status" style={{ alignItems: 'flex-start' }}>
|
||||
<StatusBadge status={scheduleStatusMap.get(schedule.id)} />
|
||||
<StatusBadge status={statusLookup[schedule.id]} />
|
||||
</Field>
|
||||
<ScheduleAmountCell amount={schedule._amount} op={schedule._amountOp} />
|
||||
{!minimal && (
|
||||
@@ -323,7 +323,7 @@ function ScheduleRow({
|
||||
export function SchedulesTable({
|
||||
isLoading,
|
||||
schedules,
|
||||
scheduleStatusMap,
|
||||
statusLookup,
|
||||
filter,
|
||||
minimal,
|
||||
allowCompleted,
|
||||
@@ -370,19 +370,11 @@ export function SchedulesTable({
|
||||
filterIncludes(payee && payee.name) ||
|
||||
filterIncludes(account && account.name) ||
|
||||
filterIncludes(amountStr) ||
|
||||
filterIncludes(scheduleStatusMap.get(schedule.id)) ||
|
||||
filterIncludes(statusLookup[schedule.id]) ||
|
||||
filterIncludes(dateStr)
|
||||
);
|
||||
});
|
||||
}, [
|
||||
payees,
|
||||
accounts,
|
||||
schedules,
|
||||
filter,
|
||||
scheduleStatusMap,
|
||||
format,
|
||||
dateFormat,
|
||||
]);
|
||||
}, [payees, accounts, schedules, filter, statusLookup, format, dateFormat]);
|
||||
|
||||
const items: readonly SchedulesTableItem[] = useMemo(() => {
|
||||
const unCompletedSchedules = filteredSchedules.filter(s => !s.completed);
|
||||
@@ -430,7 +422,7 @@ export function SchedulesTable({
|
||||
return (
|
||||
<ScheduleRow
|
||||
schedule={item as ScheduleEntity}
|
||||
{...{ scheduleStatusMap, dateFormat, onSelect, onAction, minimal }}
|
||||
{...{ statusLookup, dateFormat, onSelect, onAction, minimal }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,10 @@ import type { ScheduleItemAction } from './SchedulesTable';
|
||||
|
||||
import { Search } from '@desktop-client/components/common/Search';
|
||||
import { Page } from '@desktop-client/components/Page';
|
||||
import { useSchedules } from '@desktop-client/hooks/useSchedules';
|
||||
import {
|
||||
useSchedules,
|
||||
useScheduleStatus,
|
||||
} from '@desktop-client/hooks/useSchedules';
|
||||
import { pushModal } from '@desktop-client/modals/modalsSlice';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
|
||||
@@ -80,10 +83,13 @@ export function Schedules() {
|
||||
[],
|
||||
);
|
||||
|
||||
const { isLoading: isSchedulesLoading, data: schedules = [] } = useSchedules({
|
||||
query: q('schedules').select('*'),
|
||||
});
|
||||
|
||||
const {
|
||||
isLoading: isSchedulesLoading,
|
||||
data: { schedules, scheduleStatusMap },
|
||||
} = useSchedules({ query: q('schedules').select('*') });
|
||||
data: { statusLookup = {} },
|
||||
} = useScheduleStatus({ schedules });
|
||||
|
||||
return (
|
||||
<Page header={t('Schedules')}>
|
||||
@@ -113,7 +119,7 @@ export function Schedules() {
|
||||
isLoading={isSchedulesLoading}
|
||||
schedules={schedules}
|
||||
filter={filter}
|
||||
scheduleStatusMap={scheduleStatusMap}
|
||||
statusLookup={statusLookup}
|
||||
allowCompleted
|
||||
onSelect={onEdit}
|
||||
onAction={onAction}
|
||||
|
||||
@@ -78,9 +78,7 @@ export function SelectedTransactionsButton({
|
||||
.map(id => id.split('/')[1]);
|
||||
}, [selectedIds]);
|
||||
|
||||
const {
|
||||
data: { schedules: selectedSchedules },
|
||||
} = useSchedules({
|
||||
const { data: selectedSchedules = [] } = useSchedules({
|
||||
query: q('schedules')
|
||||
.filter({ id: { $oneof: scheduleIds } })
|
||||
.select('*'),
|
||||
|
||||
@@ -67,9 +67,7 @@ export function TransactionMenu({
|
||||
.map(id => id.split('/')[1]);
|
||||
}, [selectedIds]);
|
||||
|
||||
const {
|
||||
data: { schedules: selectedSchedules },
|
||||
} = useSchedules({
|
||||
const { data: selectedSchedules = [] } = useSchedules({
|
||||
query: q('schedules')
|
||||
.filter({ id: { $oneof: scheduleIds } })
|
||||
.select('*'),
|
||||
|
||||
@@ -764,10 +764,7 @@ function PayeeIcons({
|
||||
const { t } = useTranslation();
|
||||
|
||||
const scheduleId = transaction.schedule;
|
||||
const {
|
||||
isFetching,
|
||||
data: { schedules },
|
||||
} = useCachedSchedules();
|
||||
const { isFetching, data: schedules = [] } = useCachedSchedules();
|
||||
|
||||
if (isFetching) {
|
||||
return null;
|
||||
@@ -1095,7 +1092,7 @@ const Transaction = memo(function Transaction({
|
||||
_unmatched = false,
|
||||
} = transaction;
|
||||
|
||||
const { data: { schedules = [] } = {} } = useCachedSchedules();
|
||||
const { data: schedules = [] } = useCachedSchedules();
|
||||
const schedule = transaction.schedule
|
||||
? schedules.find(s => s.id === transaction.schedule)
|
||||
: null;
|
||||
|
||||
@@ -42,15 +42,14 @@ export function useCategoryScheduleGoalTemplateIndicator({
|
||||
'upcomingScheduledTransactionLength',
|
||||
);
|
||||
const upcomingDays = getUpcomingDays(upcomingScheduledTransactionLength);
|
||||
const { schedules, statuses: scheduleStatuses } =
|
||||
useCategoryScheduleGoalTemplates({
|
||||
category,
|
||||
});
|
||||
const { schedules, statusLookup } = useCategoryScheduleGoalTemplates({
|
||||
category,
|
||||
});
|
||||
|
||||
return useMemo<UseCategoryScheduleGoalTemplateResult>(() => {
|
||||
const schedulesToDisplay = schedules
|
||||
.filter(schedule => {
|
||||
const status = scheduleStatuses.get(schedule.id);
|
||||
const status = statusLookup[schedule.id];
|
||||
return status === 'upcoming' || status === 'due' || status === 'missed';
|
||||
})
|
||||
.filter(schedule => {
|
||||
@@ -66,8 +65,8 @@ export function useCategoryScheduleGoalTemplateIndicator({
|
||||
})
|
||||
.sort((a, b) => {
|
||||
// Display missed schedules first, then due, then upcoming.
|
||||
const aStatus = scheduleStatuses.get(a.id);
|
||||
const bStatus = scheduleStatuses.get(b.id);
|
||||
const aStatus = statusLookup[a.id];
|
||||
const bStatus = statusLookup[b.id];
|
||||
if (aStatus === 'missed' && bStatus !== 'missed') return -1;
|
||||
if (bStatus === 'missed' && aStatus !== 'missed') return 1;
|
||||
if (aStatus === 'due' && bStatus !== 'due') return -1;
|
||||
@@ -80,7 +79,7 @@ export function useCategoryScheduleGoalTemplateIndicator({
|
||||
return getScheduleStatusDescription({
|
||||
t,
|
||||
schedule: s,
|
||||
scheduleStatus: scheduleStatuses.get(s.id),
|
||||
scheduleStatus: statusLookup[s.id],
|
||||
locale,
|
||||
});
|
||||
})
|
||||
@@ -88,7 +87,7 @@ export function useCategoryScheduleGoalTemplateIndicator({
|
||||
|
||||
const schedule = schedulesToDisplay[0] || null;
|
||||
const scheduleStatus =
|
||||
(schedule ? scheduleStatuses.get(schedule.id) : null) || null;
|
||||
(schedule ? statusLookup[schedule.id] : null) || null;
|
||||
|
||||
return {
|
||||
schedule,
|
||||
@@ -98,7 +97,7 @@ export function useCategoryScheduleGoalTemplateIndicator({
|
||||
),
|
||||
description,
|
||||
};
|
||||
}, [locale, month, scheduleStatuses, schedules, t, upcomingDays]);
|
||||
}, [locale, month, statusLookup, schedules, t, upcomingDays]);
|
||||
}
|
||||
|
||||
function getScheduleStatusDescription({
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import type {
|
||||
ScheduleStatusLabelMap,
|
||||
ScheduleStatusMap,
|
||||
} from 'loot-core/shared/schedules';
|
||||
import type { CategoryEntity, ScheduleEntity } from 'loot-core/types/models';
|
||||
|
||||
import { useCachedSchedules } from './useCachedSchedules';
|
||||
import { useFeatureFlag } from './useFeatureFlag';
|
||||
import { useScheduleStatus } from './useSchedules';
|
||||
|
||||
import type { ScheduleStatusData } from '@desktop-client/schedules';
|
||||
|
||||
type ScheduleGoalDefinition = {
|
||||
type: 'schedule';
|
||||
@@ -18,30 +17,25 @@ type UseCategoryScheduleGoalTemplatesProps = {
|
||||
category?: CategoryEntity | undefined;
|
||||
};
|
||||
|
||||
type UseCategoryScheduleGoalTemplatesResult = {
|
||||
schedules: ScheduleEntity[];
|
||||
statuses: ScheduleStatusMap;
|
||||
statusLabels: ScheduleStatusLabelMap;
|
||||
type UseCategoryScheduleGoalTemplatesResult = ScheduleStatusData & {
|
||||
schedules: readonly ScheduleEntity[];
|
||||
};
|
||||
|
||||
export function useCategoryScheduleGoalTemplates({
|
||||
category,
|
||||
}: UseCategoryScheduleGoalTemplatesProps): UseCategoryScheduleGoalTemplatesResult {
|
||||
const isGoalTemplatesEnabled = useFeatureFlag('goalTemplatesEnabled');
|
||||
const { data: allSchedules = [] } = useCachedSchedules();
|
||||
const {
|
||||
data: {
|
||||
schedules: allSchedules,
|
||||
scheduleStatusMap: allStatuses,
|
||||
scheduleStatusLabelMap: allStatusLabels,
|
||||
},
|
||||
} = useCachedSchedules();
|
||||
data: { statusLookup = {}, statusLabelLookup = {} },
|
||||
} = useScheduleStatus({ schedules: allSchedules });
|
||||
|
||||
return useMemo(() => {
|
||||
if (!isGoalTemplatesEnabled || !category || !category.goal_def) {
|
||||
return {
|
||||
schedules: [],
|
||||
statuses: new Map(),
|
||||
statusLabels: new Map(),
|
||||
statusLookup: {},
|
||||
statusLabelLookup: {},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -52,8 +46,8 @@ export function useCategoryScheduleGoalTemplates({
|
||||
console.error('Failed to parse category goal_def:', e);
|
||||
return {
|
||||
schedules: [],
|
||||
statuses: new Map(),
|
||||
statusLabels: new Map(),
|
||||
statusLookup: {},
|
||||
statusLabelLookup: {},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -64,8 +58,8 @@ export function useCategoryScheduleGoalTemplates({
|
||||
if (!scheduleGoalDefinitions.length) {
|
||||
return {
|
||||
schedules: [],
|
||||
statuses: new Map(),
|
||||
statusLabels: new Map(),
|
||||
statusLookup: {},
|
||||
statusLabelLookup: {},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -75,22 +69,22 @@ export function useCategoryScheduleGoalTemplates({
|
||||
|
||||
const scheduleIds = new Set(schedules.map(s => s.id));
|
||||
|
||||
const statuses = new Map(
|
||||
[...allStatuses].filter(([id]) => scheduleIds.has(id)),
|
||||
const filteredStatusLookup = Object.fromEntries(
|
||||
Object.entries(statusLookup).filter(([id]) => scheduleIds.has(id)),
|
||||
);
|
||||
const statusLabels = new Map(
|
||||
[...allStatusLabels].filter(([id]) => scheduleIds.has(id)),
|
||||
const filteredStatusLabelLookup = Object.fromEntries(
|
||||
Object.entries(statusLabelLookup).filter(([id]) => scheduleIds.has(id)),
|
||||
);
|
||||
|
||||
return {
|
||||
schedules,
|
||||
statuses,
|
||||
statusLabels,
|
||||
statusLookup: filteredStatusLookup,
|
||||
statusLabelLookup: filteredStatusLabelLookup,
|
||||
};
|
||||
}, [
|
||||
allSchedules,
|
||||
allStatusLabels,
|
||||
allStatuses,
|
||||
statusLabelLookup,
|
||||
statusLookup,
|
||||
category,
|
||||
isGoalTemplatesEnabled,
|
||||
]);
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { IntegerAmount } from 'loot-core/shared/util';
|
||||
import type { ScheduleEntity, TransactionEntity } from 'loot-core/types/models';
|
||||
|
||||
import { useCachedSchedules } from './useCachedSchedules';
|
||||
import { useScheduleStatus } from './useSchedules';
|
||||
import { useSyncedPref } from './useSyncedPref';
|
||||
import { calculateRunningBalancesBottomUp } from './useTransactions';
|
||||
|
||||
@@ -42,8 +43,11 @@ export function usePreviewTransactions({
|
||||
const {
|
||||
isFetching: isSchedulesLoading,
|
||||
error: scheduleQueryError,
|
||||
data: { schedules, scheduleStatusMap },
|
||||
data: schedules = [],
|
||||
} = useCachedSchedules();
|
||||
const {
|
||||
data: { statusLookup },
|
||||
} = useScheduleStatus({ schedules });
|
||||
const [isLoading, setIsLoading] = useState(isSchedulesLoading);
|
||||
const [error, setError] = useState<Error | undefined>(undefined);
|
||||
const [runningBalances, setRunningBalances] = useState<
|
||||
@@ -65,17 +69,11 @@ export function usePreviewTransactions({
|
||||
|
||||
return computeSchedulePreviewTransactions(
|
||||
schedules,
|
||||
scheduleStatusMap,
|
||||
statusLookup,
|
||||
upcomingLength,
|
||||
filter,
|
||||
);
|
||||
}, [
|
||||
filter,
|
||||
isSchedulesLoading,
|
||||
schedules,
|
||||
scheduleStatusMap,
|
||||
upcomingLength,
|
||||
]);
|
||||
}, [filter, isSchedulesLoading, schedules, statusLookup, upcomingLength]);
|
||||
|
||||
useEffect(() => {
|
||||
let isUnmounted = false;
|
||||
@@ -100,10 +98,7 @@ export function usePreviewTransactions({
|
||||
if (!isUnmounted) {
|
||||
const withDefaults = newTrans.map(t => ({
|
||||
...t,
|
||||
category:
|
||||
t.schedule != null
|
||||
? scheduleStatusMap.get(t.schedule)
|
||||
: undefined,
|
||||
category: t.schedule != null ? statusLookup[t.schedule] : undefined,
|
||||
schedule: t.schedule,
|
||||
subtransactions: t.subtransactions?.map(
|
||||
(st: TransactionEntity) => ({
|
||||
@@ -145,7 +140,7 @@ export function usePreviewTransactions({
|
||||
return () => {
|
||||
isUnmounted = true;
|
||||
};
|
||||
}, [scheduleTransactions, schedules, scheduleStatusMap, upcomingLength]);
|
||||
}, [scheduleTransactions, schedules, statusLookup, upcomingLength]);
|
||||
|
||||
const returnError = error || scheduleQueryError;
|
||||
return {
|
||||
|
||||
@@ -1,54 +1,39 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { UseQueryResult } from '@tanstack/react-query';
|
||||
|
||||
import { q } from 'loot-core/shared/query';
|
||||
import type { Query } from 'loot-core/shared/query';
|
||||
import type { AccountEntity } from 'loot-core/types/models';
|
||||
import type { ScheduleEntity } from 'loot-core/types/models';
|
||||
|
||||
import { useSyncedPref } from './useSyncedPref';
|
||||
|
||||
import { accountFilter } from '@desktop-client/queries';
|
||||
import { scheduleQueries } from '@desktop-client/schedules';
|
||||
import type { ScheduleData } from '@desktop-client/schedules';
|
||||
import type { ScheduleStatusData } from '@desktop-client/schedules';
|
||||
|
||||
export type UseSchedulesProps = {
|
||||
query?: Query;
|
||||
};
|
||||
export type UseSchedulesResult = UseQueryResult<ScheduleData>;
|
||||
export type UseSchedulesResult = UseQueryResult<ScheduleEntity[]>;
|
||||
|
||||
export function useSchedules({
|
||||
query,
|
||||
}: UseSchedulesProps = {}): UseSchedulesResult {
|
||||
return useQuery(scheduleQueries.aql({ query }));
|
||||
}
|
||||
|
||||
type UseScheduleStatusProps = {
|
||||
schedules: ScheduleEntity[];
|
||||
};
|
||||
|
||||
type UseScheduleStatusResult = UseQueryResult<ScheduleStatusData>;
|
||||
|
||||
export function useScheduleStatus({
|
||||
schedules,
|
||||
}: UseScheduleStatusProps): UseScheduleStatusResult {
|
||||
const [upcomingLength] = useSyncedPref('upcomingScheduledTransactionLength');
|
||||
return useQuery(
|
||||
scheduleQueries.aql({
|
||||
query,
|
||||
statusOptions: { enabled: true, upcomingLength },
|
||||
scheduleQueries.statuses({
|
||||
schedules,
|
||||
upcomingLength,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function getSchedulesQuery(
|
||||
view?: AccountEntity['id'] | 'onbudget' | 'offbudget' | 'uncategorized',
|
||||
) {
|
||||
const filterByAccount = accountFilter(view, '_account');
|
||||
const filterByPayee = accountFilter(view, '_payee.transfer_acct');
|
||||
|
||||
let query = q('schedules')
|
||||
.select('*')
|
||||
.filter({
|
||||
$and: [{ '_account.closed': false }],
|
||||
});
|
||||
|
||||
if (view) {
|
||||
if (view === 'uncategorized') {
|
||||
query = query.filter({ next_date: null });
|
||||
} else {
|
||||
query = query.filter({
|
||||
$or: [filterByAccount, filterByPayee],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return query.orderBy({ next_date: 'desc' });
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { queryOptions } from '@tanstack/react-query';
|
||||
|
||||
import { q } from 'loot-core/shared/query';
|
||||
import type { Query } from 'loot-core/shared/query';
|
||||
import {
|
||||
getHasTransactionsQuery,
|
||||
@@ -7,11 +8,16 @@ import {
|
||||
getStatusLabel,
|
||||
} from 'loot-core/shared/schedules';
|
||||
import type {
|
||||
ScheduleStatusLabelMap,
|
||||
ScheduleStatusMap,
|
||||
ScheduleStatusLabelLookup,
|
||||
ScheduleStatusLookup,
|
||||
} from 'loot-core/shared/schedules';
|
||||
import type { ScheduleEntity, TransactionEntity } from 'loot-core/types/models';
|
||||
import type {
|
||||
AccountEntity,
|
||||
ScheduleEntity,
|
||||
TransactionEntity,
|
||||
} from 'loot-core/types/models';
|
||||
|
||||
import { accountFilter } from '@desktop-client/queries';
|
||||
import { aqlQuery } from '@desktop-client/queries/aqlQuery';
|
||||
|
||||
export type ScheduleData = ScheduleStatusData & {
|
||||
@@ -19,25 +25,21 @@ export type ScheduleData = ScheduleStatusData & {
|
||||
};
|
||||
|
||||
export type ScheduleStatusData = {
|
||||
scheduleStatusMap: ScheduleStatusMap;
|
||||
scheduleStatusLabelMap: ScheduleStatusLabelMap;
|
||||
statusLookup: ScheduleStatusLookup;
|
||||
statusLabelLookup: ScheduleStatusLabelLookup;
|
||||
};
|
||||
|
||||
type AqlOptions = {
|
||||
query?: Query;
|
||||
statusOptions?: {
|
||||
enabled: boolean;
|
||||
upcomingLength: string;
|
||||
};
|
||||
};
|
||||
|
||||
export const scheduleQueries = {
|
||||
all: () => ['schedules'],
|
||||
lists: () => [...scheduleQueries.all(), 'lists'],
|
||||
aql: ({ query, statusOptions }: AqlOptions) =>
|
||||
queryOptions<ScheduleData>({
|
||||
aql: ({ query }: AqlOptions) =>
|
||||
queryOptions<ScheduleEntity[]>({
|
||||
queryKey: [...scheduleQueries.lists(), query],
|
||||
queryFn: async ({ client }) => {
|
||||
queryFn: async () => {
|
||||
if (!query) {
|
||||
// Shouldn't happen because of the enabled flag, but needed to satisfy TS
|
||||
throw new Error('No query provided.');
|
||||
@@ -45,32 +47,10 @@ export const scheduleQueries = {
|
||||
const { data: schedules }: { data: ScheduleEntity[] } =
|
||||
await aqlQuery(query);
|
||||
|
||||
if (statusOptions?.enabled) {
|
||||
const statuses = await client.ensureQueryData(
|
||||
scheduleQueries.statuses({
|
||||
schedules,
|
||||
upcomingLength: statusOptions.upcomingLength,
|
||||
}),
|
||||
);
|
||||
return {
|
||||
schedules,
|
||||
scheduleStatusMap: statuses.scheduleStatusMap,
|
||||
scheduleStatusLabelMap: statuses.scheduleStatusLabelMap,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
schedules,
|
||||
scheduleStatusMap: new Map(),
|
||||
scheduleStatusLabelMap: new Map(),
|
||||
};
|
||||
return schedules;
|
||||
},
|
||||
enabled: !!query,
|
||||
placeholderData: {
|
||||
schedules: [],
|
||||
scheduleStatusMap: new Map(),
|
||||
scheduleStatusLabelMap: new Map(),
|
||||
},
|
||||
placeholderData: [],
|
||||
}),
|
||||
statuses: ({
|
||||
schedules,
|
||||
@@ -89,7 +69,7 @@ export const scheduleQueries = {
|
||||
transactions.filter(Boolean).map(trans => trans.schedule),
|
||||
);
|
||||
|
||||
const scheduleStatusMap: ScheduleStatusMap = new Map(
|
||||
const statusLookup: ScheduleStatusLookup = Object.fromEntries(
|
||||
schedules.map(s => [
|
||||
s.id,
|
||||
getStatus(
|
||||
@@ -101,15 +81,40 @@ export const scheduleQueries = {
|
||||
]),
|
||||
);
|
||||
|
||||
const scheduleStatusLabelMap: ScheduleStatusLabelMap = new Map(
|
||||
[...scheduleStatusMap.keys()].map(key => [
|
||||
const statusLabelLookup: ScheduleStatusLabelLookup = Object.fromEntries(
|
||||
Object.keys(statusLookup).map(key => [
|
||||
key,
|
||||
getStatusLabel(scheduleStatusMap.get(key) || ''),
|
||||
getStatusLabel(statusLookup[key] || ''),
|
||||
]),
|
||||
);
|
||||
|
||||
return { scheduleStatusMap, scheduleStatusLabelMap };
|
||||
return { statusLookup, statusLabelLookup };
|
||||
},
|
||||
enabled: schedules.length > 0,
|
||||
}),
|
||||
};
|
||||
|
||||
export function schedulesViewQuery(
|
||||
view?: AccountEntity['id'] | 'onbudget' | 'offbudget' | 'uncategorized',
|
||||
) {
|
||||
const filterByAccount = accountFilter(view, '_account');
|
||||
const filterByPayee = accountFilter(view, '_payee.transfer_acct');
|
||||
|
||||
let query = q('schedules')
|
||||
.select('*')
|
||||
.filter({
|
||||
$and: [{ '_account.closed': false }],
|
||||
});
|
||||
|
||||
if (view) {
|
||||
if (view === 'uncategorized') {
|
||||
query = query.filter({ next_date: null });
|
||||
} else {
|
||||
query = query.filter({
|
||||
$or: [filterByAccount, filterByPayee],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return query.orderBy({ next_date: 'desc' });
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
getStatus,
|
||||
getUpcomingDays,
|
||||
} from './schedules';
|
||||
import type { ScheduleStatusMap } from './schedules';
|
||||
import type { ScheduleStatusLookup } from './schedules';
|
||||
|
||||
i18next.init({
|
||||
lng: 'en',
|
||||
@@ -483,7 +483,7 @@ describe('schedules', () => {
|
||||
],
|
||||
});
|
||||
|
||||
const statuses: ScheduleStatusMap = new Map([['sched-1', 'missed']]);
|
||||
const statuses: ScheduleStatusLookup = new Map([['sched-1', 'missed']]);
|
||||
const result = computeSchedulePreviewTransactions(
|
||||
[schedule],
|
||||
statuses,
|
||||
@@ -508,7 +508,7 @@ describe('schedules', () => {
|
||||
],
|
||||
});
|
||||
|
||||
const statuses: ScheduleStatusMap = new Map([['sched-1', 'missed']]);
|
||||
const statuses: ScheduleStatusLookup = new Map([['sched-1', 'missed']]);
|
||||
const result = computeSchedulePreviewTransactions(
|
||||
[schedule],
|
||||
statuses,
|
||||
@@ -527,7 +527,9 @@ describe('schedules', () => {
|
||||
_conditions: [{ field: 'date', op: 'is', value: '2017-01-03' }],
|
||||
});
|
||||
|
||||
const statuses: ScheduleStatusMap = new Map([['sched-1', 'upcoming']]);
|
||||
const statuses: ScheduleStatusLookup = new Map([
|
||||
['sched-1', 'upcoming'],
|
||||
]);
|
||||
const result = computeSchedulePreviewTransactions(
|
||||
[schedule],
|
||||
statuses,
|
||||
@@ -551,7 +553,7 @@ describe('schedules', () => {
|
||||
],
|
||||
});
|
||||
|
||||
const statuses: ScheduleStatusMap = new Map([['sched-1', 'paid']]);
|
||||
const statuses: ScheduleStatusLookup = new Map([['sched-1', 'paid']]);
|
||||
const result = computeSchedulePreviewTransactions(
|
||||
[schedule],
|
||||
statuses,
|
||||
|
||||
@@ -474,17 +474,17 @@ export function scheduleIsRecurring(dateCond: Condition | null) {
|
||||
return value.type === 'recur';
|
||||
}
|
||||
|
||||
export type ScheduleStatusMap = Map<ScheduleEntity['id'], ScheduleStatus>;
|
||||
export type ScheduleStatusLabelMap = Map<
|
||||
export type ScheduleStatusLookup = Record<ScheduleEntity['id'], ScheduleStatus>;
|
||||
export type ScheduleStatusLabelLookup = Record<
|
||||
ScheduleEntity['id'],
|
||||
ScheduleStatusLabel
|
||||
>;
|
||||
|
||||
export function isForPreview(
|
||||
schedule: ScheduleEntity,
|
||||
statusMap: ScheduleStatusMap,
|
||||
statusMap: ScheduleStatusLookup,
|
||||
) {
|
||||
const status = statusMap.get(schedule.id);
|
||||
const status = statusMap[schedule.id];
|
||||
return (
|
||||
!schedule.completed &&
|
||||
['due', 'upcoming', 'missed', 'paid'].includes(status!)
|
||||
@@ -493,7 +493,7 @@ export function isForPreview(
|
||||
|
||||
export function computeSchedulePreviewTransactions(
|
||||
schedules: readonly ScheduleEntity[],
|
||||
statuses: ScheduleStatusMap,
|
||||
statuses: ScheduleStatusLookup,
|
||||
upcomingLength?: string,
|
||||
filter?: (schedule: ScheduleEntity) => boolean,
|
||||
) {
|
||||
@@ -515,7 +515,7 @@ export function computeSchedulePreviewTransactions(
|
||||
schedule._conditions,
|
||||
);
|
||||
|
||||
const status = statuses.get(schedule.id);
|
||||
const status = statuses[schedule.id];
|
||||
const isRecurring = scheduleIsRecurring(dateConditions);
|
||||
|
||||
const dates = [schedule.next_date];
|
||||
|
||||
Reference in New Issue
Block a user