Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
th3c0d3br34ker authored Sep 19, 2023
2 parents 8e61da4 + ddb78af commit 4c1f19e
Show file tree
Hide file tree
Showing 20 changed files with 308 additions and 170 deletions.
3 changes: 0 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@
"source-map-support": "^0.5.21",
"typescript": "^5.0.2"
},
"resolutions": {
"react-error-overlay": "6.0.9"
},
"engines": {
"node": ">=18.0.0"
},
Expand Down
Binary file modified packages/desktop-client/public/apple-touch-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useState, useMemo } from 'react';
import { Link } from 'react-router-dom';
import PullToRefresh from 'react-simple-pull-to-refresh';

import { useActions } from '../../hooks/useActions';
import Add from '../../icons/v1/Add';
Expand All @@ -12,6 +11,7 @@ import InputWithContent from '../common/InputWithContent';
import Label from '../common/Label';
import Text from '../common/Text';
import View from '../common/View';
import PullToRefresh from '../responsive/PullToRefresh';
import CellValue from '../spreadsheet/CellValue';
import { TransactionList } from '../transactions/MobileTransaction';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import PullToRefresh from 'react-simple-pull-to-refresh';

import * as queries from 'loot-core/src/client/queries';

Expand All @@ -14,6 +13,7 @@ import Text from '../common/Text';
import TextOneLine from '../common/TextOneLine';
import View from '../common/View';
import { Page } from '../Page';
import PullToRefresh from '../responsive/PullToRefresh';
import CellValue from '../spreadsheet/CellValue';

function AccountHeader({ name, amount, style = {} }) {
Expand Down
2 changes: 1 addition & 1 deletion packages/desktop-client/src/components/common/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type MenuProps = {
header?: ReactNode;
footer?: ReactNode;
items: Array<MenuItem | typeof Menu.line>;
onMenuSelect;
onMenuSelect: (itemName: MenuItem['name']) => void;
};

export default function Menu({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React, { type ComponentProps } from 'react';
import BasePullToRefresh from 'react-simple-pull-to-refresh';

import { css } from 'glamor';

type PullToRefreshProps = ComponentProps<typeof BasePullToRefresh>;

export default function PullToRefresh(props: PullToRefreshProps) {
return (
<div style={{ overflow: 'auto' }}>
<BasePullToRefresh
pullDownThreshold={80}
resistance={2}
className={String(
css({
'& .ptr__pull-down': {
textAlign: 'center',
},
'& .ptr__children': {
overflow: 'hidden auto',
},
}),
)}
{...props}
/>
</div>
);
}
187 changes: 126 additions & 61 deletions packages/desktop-client/src/components/schedules/SchedulesTable.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import React, { useState, useMemo } from 'react';
import React, { useState, useMemo, type CSSProperties } from 'react';
import { useSelector } from 'react-redux';

import { useCachedAccounts } from 'loot-core/src/client/data-hooks/accounts';
import { useCachedPayees } from 'loot-core/src/client/data-hooks/payees';
import * as monthUtils from 'loot-core/src/shared/months';
import {
type ScheduleStatusType,
type ScheduleStatuses,
} from 'loot-core/src/client/data-hooks/schedules';
import { format as monthUtilFormat } from 'loot-core/src/shared/months';
import { getScheduledAmount } from 'loot-core/src/shared/schedules';
import { integerToCurrency } from 'loot-core/src/shared/util';
import { type ScheduleEntity } from 'loot-core/src/types/models';

import DotsHorizontalTriple from '../../icons/v1/DotsHorizontalTriple';
import Check from '../../icons/v2/Check';
Expand All @@ -21,10 +26,73 @@ import DisplayId from '../util/DisplayId';

import { StatusBadge } from './StatusBadge';

export let ROW_HEIGHT = 43;
type SchedulesTableProps = {
schedules: ScheduleEntity[];
statuses: ScheduleStatuses;
filter: string;
allowCompleted: boolean;
onSelect: (id: ScheduleEntity['id']) => void;
onAction: (actionName: ScheduleItemAction, id: ScheduleEntity['id']) => void;
style: CSSProperties;
minimal?: boolean;
tableStyle?: CSSProperties;
};

function OverflowMenu({ schedule, status, onAction }) {
let [open, setOpen] = useState(false);
type CompletedScheduleItem = { id: 'show-completed' };
type SchedulesTableItem = ScheduleEntity | CompletedScheduleItem;

export type ScheduleItemAction =
| 'post-transaction'
| 'skip'
| 'complete'
| 'restart'
| 'delete';

export const ROW_HEIGHT = 43;

function OverflowMenu({
schedule,
status,
onAction,
}: {
schedule: ScheduleEntity;
status: ScheduleStatusType;
onAction: SchedulesTableProps['onAction'];
}) {
const [open, setOpen] = useState(false);

const getMenuItems = () => {
const menuItems: { name: ScheduleItemAction; text: string }[] = [];

if (status === 'due') {
menuItems.push({
name: 'post-transaction',
text: 'Post transaction',
});
}

if (status === 'completed') {
menuItems.push({
name: 'restart',
text: 'Restart',
});
} else {
menuItems.push(
{
name: 'skip',
text: 'Skip next date',
},
{
name: 'complete',
text: 'Complete',
},
);
}

menuItems.push({ name: 'delete', text: 'Delete' });

return menuItems;
};

return (
<View>
Expand All @@ -49,34 +117,28 @@ function OverflowMenu({ schedule, status, onAction }) {
onClose={() => setOpen(false)}
>
<Menu
onMenuSelect={name => {
onMenuSelect={(name: ScheduleItemAction) => {
onAction(name, schedule.id);
setOpen(false);
}}
items={[
status === 'due' && {
name: 'post-transaction',
text: 'Post transaction',
},
...(schedule.completed
? [{ name: 'restart', text: 'Restart' }]
: [
{ name: 'skip', text: 'Skip next date' },
{ name: 'complete', text: 'Complete' },
]),
{ name: 'delete', text: 'Delete' },
]}
items={getMenuItems()}
/>
</Tooltip>
)}
</View>
);
}

export function ScheduleAmountCell({ amount, op }) {
let num = getScheduledAmount(amount);
let str = integerToCurrency(Math.abs(num || 0));
let isApprox = op === 'isapprox' || op === 'isbetween';
export function ScheduleAmountCell({
amount,
op,
}: {
amount: ScheduleEntity['_amount'];
op: ScheduleEntity['_amountOp'];
}) {
const num = getScheduledAmount(amount);
const str = integerToCurrency(Math.abs(num || 0));
const isApprox = op === 'isapprox' || op === 'isbetween';

return (
<Cell
Expand Down Expand Up @@ -129,38 +191,38 @@ export function SchedulesTable({
onSelect,
onAction,
tableStyle,
}) {
let dateFormat = useSelector(state => {
}: SchedulesTableProps) {
const dateFormat = useSelector(state => {
return state.prefs.local.dateFormat || 'MM/dd/yyyy';
});

let [showCompleted, setShowCompleted] = useState(false);
const [showCompleted, setShowCompleted] = useState(false);

let payees = useCachedPayees();
let accounts = useCachedAccounts();
const payees = useCachedPayees();
const accounts = useCachedAccounts();

let filteredSchedules = useMemo(() => {
const filteredSchedules = useMemo(() => {
if (!filter) {
return schedules;
}
const filterIncludes = str =>
const filterIncludes = (str: string) =>
str
? str.toLowerCase().includes(filter.toLowerCase()) ||
filter.toLowerCase().includes(str.toLowerCase())
: false;

return schedules.filter(schedule => {
let payee = payees.find(p => schedule._payee === p.id);
let account = accounts.find(a => schedule._account === a.id);
let amount = getScheduledAmount(schedule._amount);
let amountStr =
const payee = payees.find(p => schedule._payee === p.id);
const account = accounts.find(a => schedule._account === a.id);
const amount = getScheduledAmount(schedule._amount);
const amountStr =
(schedule._amountOp === 'isapprox' || schedule._amountOp === 'isbetween'
? '~'
: '') +
(amount > 0 ? '+' : '') +
integerToCurrency(Math.abs(amount || 0));
let dateStr = schedule.next_date
? monthUtils.format(schedule.next_date, dateFormat)
const dateStr = schedule.next_date
? monthUtilFormat(schedule.next_date, dateFormat)
: null;

return (
Expand All @@ -174,26 +236,29 @@ export function SchedulesTable({
});
}, [schedules, filter, statuses]);

let items = useMemo(() => {
const items: SchedulesTableItem[] = useMemo(() => {
const unCompletedSchedules = filteredSchedules.filter(s => !s.completed);

if (!allowCompleted) {
return filteredSchedules.filter(s => !s.completed);
return unCompletedSchedules;
}
if (showCompleted) {
return filteredSchedules;
}
let arr = filteredSchedules.filter(s => !s.completed);
if (filteredSchedules.find(s => s.completed)) {
arr.push({ type: 'show-completed' });
}
return arr;

const hasCompletedSchedule = filteredSchedules.find(s => s.completed);

if (!hasCompletedSchedule) return unCompletedSchedules;

return [...unCompletedSchedules, { id: 'show-completed' }];
}, [filteredSchedules, showCompleted, allowCompleted]);

function renderSchedule({ item }) {
function renderSchedule({ schedule }: { schedule: ScheduleEntity }) {
return (
<Row
height={ROW_HEIGHT}
inset={15}
onClick={() => onSelect(item.id)}
onClick={() => onSelect(schedule.id)}
style={{
cursor: 'pointer',
backgroundColor: theme.tableBackground,
Expand All @@ -204,42 +269,42 @@ export function SchedulesTable({
<Field width="flex" name="name">
<Text
style={
item.name == null
schedule.name == null
? { color: theme.buttonNormalDisabledText }
: null
}
title={item.name ? item.name : ''}
title={schedule.name ? schedule.name : ''}
>
{item.name ? item.name : 'None'}
{schedule.name ? schedule.name : 'None'}
</Text>
</Field>
<Field width="flex" name="payee">
<DisplayId type="payees" id={item._payee} />
<DisplayId type="payees" id={schedule._payee} />
</Field>
<Field width="flex" name="account">
<DisplayId type="accounts" id={item._account} />
<DisplayId type="accounts" id={schedule._account} />
</Field>
<Field width={110} name="date">
{item.next_date
? monthUtils.format(item.next_date, dateFormat)
{schedule.next_date
? monthUtilFormat(schedule.next_date, dateFormat)
: null}
</Field>
<Field width={120} name="status" style={{ alignItems: 'flex-start' }}>
<StatusBadge status={statuses.get(item.id)} />
<StatusBadge status={statuses.get(schedule.id)} />
</Field>
<ScheduleAmountCell amount={item._amount} op={item._amountOp} />
<ScheduleAmountCell amount={schedule._amount} op={schedule._amountOp} />
{!minimal && (
<Field width={80} style={{ textAlign: 'center' }}>
{item._date && item._date.frequency && (
{schedule._date && schedule._date.frequency && (
<Check style={{ width: 13, height: 13 }} />
)}
</Field>
)}
{!minimal && (
<Field width={40} name="actions">
<OverflowMenu
schedule={item}
status={statuses.get(item.id)}
schedule={schedule}
status={statuses.get(schedule.id)}
onAction={onAction}
/>
</Field>
Expand All @@ -248,8 +313,8 @@ export function SchedulesTable({
);
}

function renderItem({ item }) {
if (item.type === 'show-completed') {
function renderItem({ item }: { item: SchedulesTableItem }) {
if (item.id === 'show-completed') {
return (
<Row
height={ROW_HEIGHT}
Expand All @@ -274,7 +339,7 @@ export function SchedulesTable({
</Row>
);
}
return renderSchedule({ item });
return renderSchedule({ schedule: item as ScheduleEntity });
}

return (
Expand All @@ -300,7 +365,7 @@ export function SchedulesTable({
backgroundColor="transparent"
version="v2"
style={{ flex: 1, backgroundColor: 'transparent', ...style }}
items={items}
items={items as ScheduleEntity[]}
renderItem={renderItem}
renderEmpty={filter ? 'No matching schedules' : 'No schedules'}
allowPopupsEscape={items.length < 6}
Expand Down
Loading

0 comments on commit 4c1f19e

Please sign in to comment.