diff --git a/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.test.tsx b/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.test.tsx
index 0bc3f317b3a..e248b8cf2f7 100644
--- a/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.test.tsx
+++ b/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.test.tsx
@@ -38,7 +38,7 @@ function makePayee(name: string, options?: { favorite: boolean }): PayeeEntity {
return {
id: name.toLowerCase() + '-id',
name,
- favorite: options?.favorite ?? false,
+ favorite: options?.favorite ? 1 : 0,
transfer_acct: undefined,
};
}
diff --git a/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx b/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx
index ea4d4791394..e567804f658 100644
--- a/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx
+++ b/packages/desktop-client/src/components/autocomplete/PayeeAutocomplete.tsx
@@ -316,7 +316,7 @@ export function PayeeAutocomplete({
return filteredSuggestions;
}
- return [{ id: 'new', favorite: false, name: '' }, ...filteredSuggestions];
+ return [{ id: 'new', favorite: 0, name: '' }, ...filteredSuggestions];
}, [commonPayees, payees, focusTransferPayees, accounts, hasPayeeInput]);
const dispatch = useDispatch();
diff --git a/packages/desktop-client/src/components/modals/LoadBackupModal.jsx b/packages/desktop-client/src/components/modals/LoadBackupModal.tsx
similarity index 71%
rename from packages/desktop-client/src/components/modals/LoadBackupModal.jsx
rename to packages/desktop-client/src/components/modals/LoadBackupModal.tsx
index 84c8f68ff56..b40b659d087 100644
--- a/packages/desktop-client/src/components/modals/LoadBackupModal.jsx
+++ b/packages/desktop-client/src/components/modals/LoadBackupModal.tsx
@@ -1,7 +1,8 @@
-import React, { Component, useState, useEffect } from 'react';
+import React, { useState, useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { loadBackup, makeBackup } from 'loot-core/client/actions';
+import { type Backup } from 'loot-core/server/backups';
import { send, listen, unlisten } from 'loot-core/src/platform/client/fetch';
import { useMetadataPref } from '../../hooks/useMetadataPref';
@@ -13,48 +14,47 @@ import { Text } from '../common/Text';
import { View } from '../common/View';
import { Row, Cell } from '../table';
-class BackupTable extends Component {
- state = { hoveredBackup: null };
+type BackupTableProps = {
+ backups: Backup[];
+ onSelect: (backupId: string) => void;
+};
- onHover = id => {
- this.setState({ hoveredBackup: id });
- };
-
- render() {
- const { backups, onSelect } = this.props;
- const { hoveredBackup } = this.state;
-
- return (
- this.onHover(null)}
- >
- {backups.map((backup, idx) => (
- this.onHover(backup.id)}
- onClick={() => onSelect(backup.id)}
- style={{ cursor: 'pointer' }}
- >
- |
-
- ))}
-
- );
- }
+function BackupTable({ backups, onSelect }: BackupTableProps) {
+ return (
+
+ {backups.map((backup, idx) => (
+ onSelect(backup.id)}
+ style={{ cursor: 'pointer' }}
+ >
+ |
+
+ ))}
+
+ );
}
-export function LoadBackupModal({ budgetId, watchUpdates, backupDisabled }) {
+type LoadBackupModalProps = {
+ budgetId: string;
+ watchUpdates: boolean;
+ backupDisabled: boolean;
+};
+
+export function LoadBackupModal({
+ budgetId,
+ watchUpdates,
+ backupDisabled,
+}: LoadBackupModalProps) {
const dispatch = useDispatch();
- const [backups, setBackups] = useState([]);
+ const [backups, setBackups] = useState([]);
const [prefsBudgetId] = useMetadataPref('id');
- const budgetIdToLoad = budgetId || prefsBudgetId;
+ const budgetIdToLoad = budgetId ?? prefsBudgetId;
useEffect(() => {
send('backups-get', { id: budgetIdToLoad }).then(setBackups);
@@ -63,12 +63,16 @@ export function LoadBackupModal({ budgetId, watchUpdates, backupDisabled }) {
useEffect(() => {
if (watchUpdates) {
listen('backups-updated', setBackups);
- return () => unlisten('backups-updated', setBackups);
+ return () => unlisten('backups-updated');
}
}, [watchUpdates]);
- const latestBackup = backups.find(backup => backup.isLatest);
- const previousBackups = backups.filter(backup => !backup.isLatest);
+ const latestBackup = backups.find(backup =>
+ 'isLatest' in backup ? backup.isLatest : false,
+ );
+ const previousBackups = backups.filter(
+ backup => !('isLatest' in backup ? backup.isLatest : false),
+ );
return (
diff --git a/packages/desktop-client/src/components/payees/ManagePayees.jsx b/packages/desktop-client/src/components/payees/ManagePayees.jsx
deleted file mode 100644
index 2843bc644ba..00000000000
--- a/packages/desktop-client/src/components/payees/ManagePayees.jsx
+++ /dev/null
@@ -1,355 +0,0 @@
-import {
- forwardRef,
- useState,
- useEffect,
- useLayoutEffect,
- useRef,
- useMemo,
- useCallback,
- useImperativeHandle,
-} from 'react';
-
-import memoizeOne from 'memoize-one';
-
-import { getNormalisedString } from 'loot-core/src/shared/normalisation';
-import { groupById } from 'loot-core/src/shared/util';
-
-import {
- useSelected,
- SelectedProvider,
- useSelectedDispatch,
- useSelectedItems,
-} from '../../hooks/useSelected';
-import { useStableCallback } from '../../hooks/useStableCallback';
-import { SvgExpandArrow } from '../../icons/v0';
-import { theme } from '../../style';
-import { Button } from '../common/Button2';
-import { Popover } from '../common/Popover';
-import { Search } from '../common/Search';
-import { View } from '../common/View';
-import { TableHeader, Cell, SelectCell, useTableNavigator } from '../table';
-
-import { PayeeMenu } from './PayeeMenu';
-import { PayeeTable } from './PayeeTable';
-
-const getPayeesById = memoizeOne(payees => groupById(payees));
-
-function plural(count, singleText, pluralText) {
- return count === 1 ? singleText : pluralText;
-}
-
-function PayeeTableHeader() {
- const borderColor = theme.tableborder;
- const dispatchSelected = useSelectedDispatch();
- const selectedItems = useSelectedItems();
-
- return (
-
-
- 0}
- onSelect={e =>
- dispatchSelected({ type: 'select-all', isRangeSelect: e.shiftKey })
- }
- />
- |
-
-
- );
-}
-
-function EmptyMessage({ text, style }) {
- return (
-
- {text}
-
- );
-}
-
-export const ManagePayees = forwardRef(
- (
- {
- payees,
- ruleCounts,
- orphanedPayees,
- categoryGroups,
- initialSelectedIds,
- ruleActions,
- onBatchChange,
- onViewRules,
- onCreateRule,
- ...props
- },
- ref,
- ) => {
- const [highlightedRows, setHighlightedRows] = useState(null);
- const [filter, setFilter] = useState('');
- const table = useRef(null);
- const scrollTo = useRef(null);
- const triggerRef = useRef(null);
- const resetAnimation = useRef(false);
- const [orphanedOnly, setOrphanedOnly] = useState(false);
-
- const filteredPayees = useMemo(() => {
- let filtered = payees;
- if (filter) {
- filtered = filtered.filter(p =>
- getNormalisedString(p.name).includes(getNormalisedString(filter)),
- );
- }
- if (orphanedOnly) {
- filtered = filtered.filter(p =>
- orphanedPayees.map(o => o.id).includes(p.id),
- );
- }
- return filtered;
- }, [payees, filter, orphanedOnly]);
-
- const selected = useSelected('payees', filteredPayees, initialSelectedIds);
-
- function applyFilter(f) {
- if (filter !== f) {
- table.current?.setRowAnimation(false);
- setFilter(f);
- resetAnimation.current = true;
- }
- }
-
- function _scrollTo(id) {
- applyFilter('');
- scrollTo.current = id;
- }
-
- useEffect(() => {
- if (resetAnimation.current) {
- // Very annoying, for some reason it's as if the table doesn't
- // actually update its contents until the next tick or
- // something? The table keeps being animated without this
- setTimeout(() => {
- table.current?.setRowAnimation(true);
- }, 0);
- resetAnimation.current = false;
- }
- });
-
- useImperativeHandle(ref, () => ({
- selectRows: (ids, scroll) => {
- tableNavigator.onEdit(null);
- selected.dispatch({ type: 'select-all', ids });
- setHighlightedRows(null);
-
- if (scroll && ids.length > 0) {
- _scrollTo(ids[0]);
- }
- },
-
- highlightRow: id => {
- tableNavigator.onEdit(null);
- setHighlightedRows(new Set([id]));
- _scrollTo(id);
- },
- }));
-
- // `highlightedRows` should only ever be true once, and we
- // immediately discard it. This triggers an animation.
- useEffect(() => {
- if (highlightedRows) {
- setHighlightedRows(null);
- }
- }, [highlightedRows]);
-
- useLayoutEffect(() => {
- if (scrollTo.current) {
- table.current.scrollTo(scrollTo.current);
- scrollTo.current = null;
- }
- });
-
- const onUpdate = useStableCallback((id, name, value) => {
- const payee = payees.find(p => p.id === id);
- if (payee[name] !== value) {
- onBatchChange({ updated: [{ id, [name]: value }] });
- }
- });
-
- const getSelectableIds = useCallback(() => {
- return filteredPayees.filter(p => p.transfer_acct == null).map(p => p.id);
- }, [filteredPayees]);
-
- function onDelete() {
- onBatchChange({ deleted: [...selected.items].map(id => ({ id })) });
- selected.dispatch({ type: 'select-none' });
- }
-
- function onFavorite() {
- const allFavorited = [...selected.items]
- .map(id => payeesById[id].favorite)
- .every(f => f === 1);
- if (allFavorited) {
- onBatchChange({
- updated: [...selected.items].map(id => ({ id, favorite: 0 })),
- });
- } else {
- onBatchChange({
- updated: [...selected.items].map(id => ({ id, favorite: 1 })),
- });
- }
- selected.dispatch({ type: 'select-none' });
- }
-
- async function onMerge() {
- const ids = [...selected.items];
- await props.onMerge(ids);
-
- tableNavigator.onEdit(ids[0], 'name');
- selected.dispatch({ type: 'select-none' });
- _scrollTo(ids[0]);
- }
-
- const buttonsDisabled = selected.items.size === 0;
-
- const tableNavigator = useTableNavigator(filteredPayees, item =>
- ['select', 'name', 'rule-count'].filter(name => {
- switch (name) {
- case 'select':
- return item.transfer_acct == null;
- default:
- return true;
- }
- }),
- );
-
- const payeesById = getPayeesById(payees);
-
- const [menuOpen, setMenuOpen] = useState(false);
-
- return (
-
-
-
-
-
- setMenuOpen(false)}
- >
- setMenuOpen(false)}
- onDelete={onDelete}
- onMerge={onMerge}
- onFavorite={onFavorite}
- />
-
-
-
- {(orphanedOnly ||
- (orphanedPayees && orphanedPayees.length > 0)) && (
-
- )}
-
-
-
-
-
-
-
-
- {filteredPayees.length === 0 ? (
-
- ) : (
-
- )}
-
-
-
- );
- },
-);
-
-ManagePayees.displayName = 'ManagePayees';
diff --git a/packages/desktop-client/src/components/payees/ManagePayees.tsx b/packages/desktop-client/src/components/payees/ManagePayees.tsx
new file mode 100644
index 00000000000..b9c37160d63
--- /dev/null
+++ b/packages/desktop-client/src/components/payees/ManagePayees.tsx
@@ -0,0 +1,284 @@
+import {
+ useState,
+ useRef,
+ useMemo,
+ useCallback,
+ type ComponentProps,
+} from 'react';
+
+import memoizeOne from 'memoize-one';
+
+import { getNormalisedString } from 'loot-core/src/shared/normalisation';
+import { groupById } from 'loot-core/src/shared/util';
+import { type PayeeEntity } from 'loot-core/types/models';
+
+import {
+ useSelected,
+ SelectedProvider,
+ useSelectedDispatch,
+ useSelectedItems,
+} from '../../hooks/useSelected';
+import { SvgExpandArrow } from '../../icons/v0';
+import { theme } from '../../style';
+import { Button } from '../common/Button2';
+import { Popover } from '../common/Popover';
+import { Search } from '../common/Search';
+import { View } from '../common/View';
+import { TableHeader, Cell, SelectCell } from '../table';
+
+import { PayeeMenu } from './PayeeMenu';
+import { PayeeTable } from './PayeeTable';
+
+const getPayeesById = memoizeOne((payees: PayeeEntity[]) => groupById(payees));
+
+function plural(count: number, singleText: string, pluralText: string) {
+ return count === 1 ? singleText : pluralText;
+}
+
+function PayeeTableHeader() {
+ const dispatchSelected = useSelectedDispatch();
+ const selectedItems = useSelectedItems();
+
+ return (
+
+
+ 0}
+ onSelect={e =>
+ dispatchSelected({ type: 'select-all', isRangeSelect: e.shiftKey })
+ }
+ />
+ |
+
+
+ );
+}
+
+type ManagePayeesProps = {
+ payees: PayeeEntity[];
+ ruleCounts: ComponentProps['ruleCounts'];
+ orphanedPayees: PayeeEntity[];
+ initialSelectedIds: string[];
+ onBatchChange: (arg: {
+ deleted?: Array<{ id: string }>;
+ updated?: Array<{ id: string; name?: string; favorite?: 0 | 1 }>;
+ }) => void;
+ onViewRules: ComponentProps['onViewRules'];
+ onCreateRule: ComponentProps['onCreateRule'];
+ onMerge: (ids: string[]) => Promise;
+};
+
+export const ManagePayees = ({
+ payees,
+ ruleCounts,
+ orphanedPayees,
+ initialSelectedIds,
+ onBatchChange,
+ onViewRules,
+ onCreateRule,
+ ...props
+}: ManagePayeesProps) => {
+ const [filter, setFilter] = useState('');
+ const table = useRef(null);
+ const triggerRef = useRef(null);
+ const [orphanedOnly, setOrphanedOnly] = useState(false);
+
+ const filteredPayees = useMemo(() => {
+ let filtered = payees;
+ if (filter) {
+ filtered = filtered.filter(p =>
+ getNormalisedString(p.name).includes(getNormalisedString(filter)),
+ );
+ }
+ if (orphanedOnly) {
+ filtered = filtered.filter(p =>
+ orphanedPayees.map(o => o.id).includes(p.id),
+ );
+ }
+ return filtered;
+ }, [payees, filter, orphanedOnly, orphanedPayees]);
+
+ const selected = useSelected('payees', filteredPayees, initialSelectedIds);
+
+ function applyFilter(f: string) {
+ if (filter !== f) {
+ setFilter(f);
+ }
+ }
+
+ const onUpdate = useCallback(
+ (
+ id: PayeeEntity['id'],
+ name: T,
+ value: PayeeEntity[T],
+ ) => {
+ const payee = payees.find(p => p.id === id);
+ if (payee && payee[name] !== value) {
+ onBatchChange({ updated: [{ id, [name]: value }] });
+ }
+ },
+ [payees, onBatchChange],
+ );
+
+ const getSelectableIds = useCallback(() => {
+ return Promise.resolve(
+ filteredPayees.filter(p => p.transfer_acct == null).map(p => p.id),
+ );
+ }, [filteredPayees]);
+
+ function onDelete() {
+ onBatchChange({ deleted: [...selected.items].map(id => ({ id })) });
+ selected.dispatch({ type: 'select-none' });
+ }
+
+ function onFavorite() {
+ const allFavorited = [...selected.items]
+ .map(id => payeesById[id].favorite)
+ .every(f => f === 1);
+ if (allFavorited) {
+ onBatchChange({
+ updated: [...selected.items].map(id => ({ id, favorite: 0 })),
+ });
+ } else {
+ onBatchChange({
+ updated: [...selected.items].map(id => ({ id, favorite: 1 })),
+ });
+ }
+ selected.dispatch({ type: 'select-none' });
+ }
+
+ async function onMerge() {
+ const ids = [...selected.items];
+ await props.onMerge(ids);
+
+ selected.dispatch({ type: 'select-none' });
+ }
+
+ const buttonsDisabled = selected.items.size === 0;
+
+ const payeesById = getPayeesById(payees);
+
+ const [menuOpen, setMenuOpen] = useState(false);
+
+ return (
+
+
+
+
+
+ setMenuOpen(false)}
+ >
+ setMenuOpen(false)}
+ onDelete={onDelete}
+ onMerge={onMerge}
+ onFavorite={onFavorite}
+ />
+
+
+
+ {(orphanedOnly || (orphanedPayees && orphanedPayees.length > 0)) && (
+
+ )}
+
+
+
+
+
+
+
+
+ {filteredPayees.length === 0 ? (
+
+ No payees
+
+ ) : (
+
+ )}
+
+
+
+ );
+};
diff --git a/packages/desktop-client/src/components/payees/ManagePayeesWithData.jsx b/packages/desktop-client/src/components/payees/ManagePayeesWithData.jsx
index 55ea8bc78ae..423a41132d3 100644
--- a/packages/desktop-client/src/components/payees/ManagePayeesWithData.jsx
+++ b/packages/desktop-client/src/components/payees/ManagePayeesWithData.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect, useRef } from 'react';
+import React, { useState, useEffect } from 'react';
import { useSelector } from 'react-redux';
import { send, listen } from 'loot-core/src/platform/client/fetch';
@@ -21,7 +21,6 @@ export function ManagePayeesWithData({ initialSelectedIds }) {
const [payees, setPayees] = useState(initialPayees);
const [ruleCounts, setRuleCounts] = useState({ value: new Map() });
const [orphans, setOrphans] = useState({ value: new Map() });
- const payeesRef = useRef();
async function refetchOrphanedPayees() {
const orphs = await send('payees-get-orphaned');
@@ -120,7 +119,6 @@ export function ManagePayeesWithData({ initialSelectedIds }) {
return (
;
onDelete: () => void;
onMerge: () => Promise;
- onFavorite: () => Promise;
+ onFavorite: () => void;
onClose: () => void;
};
diff --git a/packages/desktop-client/src/components/payees/PayeeTable.tsx b/packages/desktop-client/src/components/payees/PayeeTable.tsx
index 3efbaaad8e2..08542439935 100644
--- a/packages/desktop-client/src/components/payees/PayeeTable.tsx
+++ b/packages/desktop-client/src/components/payees/PayeeTable.tsx
@@ -12,7 +12,7 @@ import { type PayeeEntity } from 'loot-core/src/types/models';
import { useSelectedItems } from '../../hooks/useSelected';
import { View } from '../common/View';
-import { Table, type TableNavigator } from '../table';
+import { Table } from '../table';
import { PayeeTableRow } from './PayeeTableRow';
@@ -23,7 +23,6 @@ type PayeeWithId = PayeeEntity & Required>;
type PayeeTableProps = {
payees: PayeeWithId[];
ruleCounts: Map;
- navigator: TableNavigator;
} & Pick<
ComponentProps,
'onUpdate' | 'onViewRules' | 'onCreateRule'
@@ -32,53 +31,46 @@ type PayeeTableProps = {
export const PayeeTable = forwardRef<
ComponentRef>,
PayeeTableProps
->(
- (
- { payees, ruleCounts, navigator, onUpdate, onViewRules, onCreateRule },
- ref,
- ) => {
- const [hovered, setHovered] = useState(null);
- const selectedItems = useSelectedItems();
+>(({ payees, ruleCounts, onUpdate, onViewRules, onCreateRule }, ref) => {
+ const [hovered, setHovered] = useState(null);
+ const selectedItems = useSelectedItems();
- useLayoutEffect(() => {
- const firstSelected = [...selectedItems][0] as string;
- if (typeof ref !== 'function') {
- ref.current.scrollTo(firstSelected, 'center');
- }
- navigator.onEdit(firstSelected, 'select');
- }, []);
+ useLayoutEffect(() => {
+ const firstSelected = [...selectedItems][0] as string;
+ if (typeof ref !== 'function') {
+ ref.current.scrollTo(firstSelected, 'center');
+ }
+ }, []);
- const onHover = useCallback(id => {
- setHovered(id);
- }, []);
+ const onHover = useCallback(id => {
+ setHovered(id);
+ }, []);
- return (
- setHovered(null)}>
- {
- return (
-
- );
- }}
- />
-
- );
- },
-);
+ return (
+ setHovered(null)}>
+ {
+ return (
+
+ );
+ }}
+ />
+
+ );
+});
PayeeTable.displayName = 'PayeeTable';
diff --git a/packages/desktop-client/src/components/payees/PayeeTableRow.tsx b/packages/desktop-client/src/components/payees/PayeeTableRow.tsx
index cd6fe5aeb2e..b716d04bc9d 100644
--- a/packages/desktop-client/src/components/payees/PayeeTableRow.tsx
+++ b/packages/desktop-client/src/components/payees/PayeeTableRow.tsx
@@ -71,10 +71,10 @@ type PayeeTableRowProps = {
focusedField: string;
onHover?: (id: PayeeEntity['id']) => void;
onEdit: (id: PayeeEntity['id'], field: string) => void;
- onUpdate: (
+ onUpdate: (
id: PayeeEntity['id'],
- field: EditablePayeeFields,
- value: unknown,
+ field: T,
+ value: PayeeEntity[T],
) => void;
onViewRules: (id: PayeeEntity['id']) => void;
onCreateRule: (id: PayeeEntity['id']) => void;
diff --git a/packages/desktop-client/src/components/table.tsx b/packages/desktop-client/src/components/table.tsx
index 655c8f954e9..b2ee1bb96e9 100644
--- a/packages/desktop-client/src/components/table.tsx
+++ b/packages/desktop-client/src/components/table.tsx
@@ -1212,7 +1212,7 @@ export const Table = forwardRef(
// @ts-expect-error fix me
Table.displayName = 'Table';
-export type TableNavigator = {
+type TableNavigator = {
onEdit: (id: T['id'], field?: string) => void;
editingId: T['id'];
focusedField: string;
diff --git a/packages/desktop-client/src/components/transactions/TransactionsTable.test.jsx b/packages/desktop-client/src/components/transactions/TransactionsTable.test.jsx
index a9644f97704..d8d58265f83 100644
--- a/packages/desktop-client/src/components/transactions/TransactionsTable.test.jsx
+++ b/packages/desktop-client/src/components/transactions/TransactionsTable.test.jsx
@@ -40,20 +40,20 @@ const payees = [
{
id: 'bob-id',
name: 'Bob',
- favorite: true,
+ favorite: 1,
transfer_acct: null,
category: null,
},
{
id: 'alice-id',
name: 'Alice',
- favorite: true,
+ favorite: 1,
transfer_acct: null,
category: null,
},
{
id: 'guy',
- favorite: false,
+ favorite: 0,
transfer_acct: null,
category: null,
name: 'This guy on the side of the road',
diff --git a/packages/desktop-client/src/hooks/useStableCallback.ts b/packages/desktop-client/src/hooks/useStableCallback.ts
deleted file mode 100644
index 9a09968750e..00000000000
--- a/packages/desktop-client/src/hooks/useStableCallback.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-// @ts-strict-ignore
-import { useRef, useLayoutEffect, useCallback } from 'react';
-
-type UseStableCallbackArg = (...args: unknown[]) => unknown;
-
-export function useStableCallback(callback: UseStableCallbackArg) {
- const callbackRef = useRef();
- const memoCallback = useCallback(
- (...args) => callbackRef.current && callbackRef.current(...args),
- [],
- );
- useLayoutEffect(() => {
- callbackRef.current = callback;
- });
- return memoCallback;
-}
diff --git a/packages/loot-core/src/shared/util.ts b/packages/loot-core/src/shared/util.ts
index 43dabd75372..04e33702c93 100644
--- a/packages/loot-core/src/shared/util.ts
+++ b/packages/loot-core/src/shared/util.ts
@@ -148,7 +148,9 @@ export function diffItems(
return { added, updated, deleted };
}
-export function groupById(data: T[]) {
+export function groupById(
+ data: T[],
+): Record {
const res: { [key: string]: T } = {};
for (let i = 0; i < data.length; i++) {
const item = data[i];
diff --git a/packages/loot-core/src/types/models/payee.d.ts b/packages/loot-core/src/types/models/payee.d.ts
index f55f04aa49f..37e3f77bb0e 100644
--- a/packages/loot-core/src/types/models/payee.d.ts
+++ b/packages/loot-core/src/types/models/payee.d.ts
@@ -4,6 +4,6 @@ export interface PayeeEntity {
id: string;
name: string;
transfer_acct?: AccountEntity['id'];
- favorite?: boolean;
+ favorite?: 1 | 0;
tombstone?: boolean;
}
diff --git a/packages/loot-core/src/types/server-events.d.ts b/packages/loot-core/src/types/server-events.d.ts
index c53004a9cab..cd0dabf8514 100644
--- a/packages/loot-core/src/types/server-events.d.ts
+++ b/packages/loot-core/src/types/server-events.d.ts
@@ -1,7 +1,8 @@
+import { type Backup } from '../server/backups';
import { type UndoState } from '../server/undo';
export interface ServerEvents {
- 'backups-updated': unknown;
+ 'backups-updated': Backup[];
'cells-changed': Array<{ name }>;
'fallback-write-error': unknown;
'finish-import': unknown;
diff --git a/upcoming-release-notes/3507.md b/upcoming-release-notes/3507.md
new file mode 100644
index 00000000000..f19343f3440
--- /dev/null
+++ b/upcoming-release-notes/3507.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [MatissJanis]
+---
+
+TypeScript: migrated `ManagePayees` and `LoadBackupModal`.