diff --git a/packages/desktop-client/src/components/Modals.tsx b/packages/desktop-client/src/components/Modals.tsx index 5974ec0e2fbcbb703968ba8932395d554012332a..5163c587f25bccb5300cb623188747246148be89 100644 --- a/packages/desktop-client/src/components/Modals.tsx +++ b/packages/desktop-client/src/components/Modals.tsx @@ -310,6 +310,7 @@ export function Modals() { modalProps={modalProps} id={options?.id || null} actions={actions} + transaction={options?.transaction || null} /> ); @@ -320,6 +321,8 @@ export function Modals() { modalProps={modalProps} actions={actions} transactionIds={options?.transactionIds} + getTransaction={options?.getTransaction} + pushModal={options?.pushModal} /> ); diff --git a/packages/desktop-client/src/components/schedules/ScheduleDetails.jsx b/packages/desktop-client/src/components/schedules/ScheduleDetails.jsx index 8459e937c033b9542fd783f113a8303053baf555..85446055b9221f805b15c943062167216b62b984 100644 --- a/packages/desktop-client/src/components/schedules/ScheduleDetails.jsx +++ b/packages/desktop-client/src/components/schedules/ScheduleDetails.jsx @@ -67,14 +67,14 @@ function updateScheduleConditions(schedule, fields) { }; } -export function ScheduleDetails({ modalProps, actions, id }) { +export function ScheduleDetails({ modalProps, actions, id, transaction }) { const adding = id == null; + const fromTrans = transaction != null; const payees = useCachedPayees({ idKey: true }); const globalDispatch = useDispatch(); const dateFormat = useSelector(state => { return state.prefs.local.dateFormat || 'MM/dd/yyyy'; }); - const [state, dispatch] = useReducer( (state, action) => { switch (action.type) { @@ -143,6 +143,11 @@ export function ScheduleDetails({ modalProps, actions, id }) { fields: { ...state.fields, ...fields }, }; case 'set-transactions': + if (fromTrans && action.transactions) { + action.transactions.sort(a => { + return transaction.id === a.id ? -1 : 1; + }); + } return { ...state, transactions: action.transactions }; case 'set-repeats': return { @@ -210,12 +215,30 @@ export function ScheduleDetails({ modalProps, actions, id }) { endOccurrences: '1', endDate: monthUtils.currentDay(), }; - const schedule = { - posts_transaction: false, - _date: date, - _conditions: [{ op: 'isapprox', field: 'date', value: date }], - _actions: [], - }; + + const schedule = fromTrans + ? { + posts_transaction: false, + _conditions: [{ op: 'isapprox', field: 'date', value: date }], + _actions: [], + _account: transaction.account, + _amount: transaction.amount, + _amountOp: 'is', + name: transaction.payee ? payees[transaction.payee].name : '', + _payee: transaction.payee ? transaction.payee : '', + _date: { + ...date, + frequency: 'monthly', + start: transaction.date, + patterns: [], + }, + } + : { + posts_transaction: false, + _date: date, + _conditions: [{ op: 'isapprox', field: 'date', value: date }], + _actions: [], + }; dispatch({ type: 'set-schedule', schedule }); } else { @@ -226,6 +249,7 @@ export function ScheduleDetails({ modalProps, actions, id }) { } } } + run(); }, []); @@ -321,7 +345,11 @@ export function ScheduleDetails({ modalProps, actions, id }) { }; }, [state.schedule, state.transactionsMode, state.fields]); - const selectedInst = useSelected('transactions', state.transactions, []); + const selectedInst = useSelected( + 'transactions', + state.transactions, + transaction ? [transaction.id] : [], + ); async function onSave() { dispatch({ type: 'form-error', error: null }); @@ -415,7 +443,6 @@ export function ScheduleDetails({ modalProps, actions, id }) { } const payee = payees ? payees[state.fields.payee] : null; - // This is derived from the date const repeats = state.fields.date ? !!state.fields.date.frequency : false; return ( diff --git a/packages/desktop-client/src/components/schedules/ScheduleLink.tsx b/packages/desktop-client/src/components/schedules/ScheduleLink.tsx index c6251c12aeb7c10f63e3d85974af07f08989a585..ed924fca29650d0938a5c4fb1e4f7c278b73ceba 100644 --- a/packages/desktop-client/src/components/schedules/ScheduleLink.tsx +++ b/packages/desktop-client/src/components/schedules/ScheduleLink.tsx @@ -4,8 +4,11 @@ import React, { useCallback, useRef, useState } from 'react'; import { useSchedules } from 'loot-core/src/client/data-hooks/schedules'; import { send } from 'loot-core/src/platform/client/fetch'; import { type Query } from 'loot-core/src/shared/query'; +import { type TransactionEntity } from 'loot-core/src/types/models'; import { type BoundActions } from '../../hooks/useActions'; +import { SvgAdd } from '../../icons/v0'; +import { Button } from '../common/Button'; import { Modal } from '../common/Modal'; import { Search } from '../common/Search'; import { Text } from '../common/Text'; @@ -14,14 +17,23 @@ import { type CommonModalProps } from '../Modals'; import { ROW_HEIGHT, SchedulesTable } from './SchedulesTable'; +type ModalParams = { + id: string; + transaction: TransactionEntity; +}; + export function ScheduleLink({ modalProps, actions, transactionIds: ids, + getTransaction, + pushModal, }: { actions: BoundActions; modalProps?: CommonModalProps; transactionIds: string[]; + getTransaction: (transactionId: string) => TransactionEntity; + pushModal: (name: string, params: ModalParams) => void; }) { const [filter, setFilter] = useState(''); @@ -45,6 +57,14 @@ export function ScheduleLink({ actions.popModal(); } + async function onCreate() { + actions.popModal(); + pushModal('schedule-edit', { + id: null, + transaction: getTransaction(ids[0]), + }); + } + return ( <Modal title="Link Schedule" size={{ width: 600 }} {...modalProps}> <View @@ -70,6 +90,16 @@ export function ScheduleLink({ value={filter} onChange={setFilter} /> + {ids.length === 1 && ( + <Button + type="primary" + style={{ marginLeft: 15, padding: '4px 10px' }} + onClick={onCreate} + > + <SvgAdd style={{ width: '20', padding: '3' }} /> + Create New + </Button> + )} </View> <View diff --git a/packages/desktop-client/src/components/transactions/SelectedTransactions.jsx b/packages/desktop-client/src/components/transactions/SelectedTransactions.jsx index d0c36098293d7610e0cc50460d280f3b9ae9e54b..fa4cffa4b9a6dba476174a8dd02f67f2bd3cde8f 100644 --- a/packages/desktop-client/src/components/transactions/SelectedTransactions.jsx +++ b/packages/desktop-client/src/components/transactions/SelectedTransactions.jsx @@ -135,6 +135,8 @@ export function SelectedTransactionsButton({ case 'link-schedule': pushModal('schedule-link', { transactionIds: [...selectedItems], + getTransaction, + pushModal, }); break; case 'unlink-schedule': diff --git a/packages/desktop-client/src/components/transactions/SimpleTransactionsTable.jsx b/packages/desktop-client/src/components/transactions/SimpleTransactionsTable.jsx index 4d6f6610a6d372e5f34955af7350a8bb8d7c46b1..ea43f6adbd69b86bb499e8a397b01e24b37d71b6 100644 --- a/packages/desktop-client/src/components/transactions/SimpleTransactionsTable.jsx +++ b/packages/desktop-client/src/components/transactions/SimpleTransactionsTable.jsx @@ -1,10 +1,10 @@ -import React, { memo, useMemo, useCallback } from 'react'; +import React, { memo, useCallback, useMemo } from 'react'; import { useSelector } from 'react-redux'; import { format as formatDate, - parseISO, isValid as isDateValid, + parseISO, } from 'date-fns'; import { @@ -14,10 +14,10 @@ import { import { integerToCurrency } from 'loot-core/src/shared/util'; import { useCategories } from '../../hooks/useCategories'; -import { useSelectedItems, useSelectedDispatch } from '../../hooks/useSelected'; +import { useSelectedDispatch, useSelectedItems } from '../../hooks/useSelected'; import { SvgArrowsSynchronize } from '../../icons/v2'; -import { theme, styles } from '../../style'; -import { Table, Row, Field, Cell, SelectCell } from '../table'; +import { styles, theme } from '../../style'; +import { Cell, Field, Row, SelectCell, Table } from '../table'; import { DisplayId } from '../util/DisplayId'; function serializeTransaction(transaction, dateFormat) { diff --git a/upcoming-release-notes/2222.md b/upcoming-release-notes/2222.md new file mode 100644 index 0000000000000000000000000000000000000000..1412f1fd83744940201898b78328ee8cb54c0c29 --- /dev/null +++ b/upcoming-release-notes/2222.md @@ -0,0 +1,6 @@ +--- +category: Features +authors: [xentara1] +--- + +Add ability to create schedules from existing transactions