* #199 Adding translation to schedules list * #199 Complete translation on EditSchedule Form * #199 Translation for status badge * #199 Minor changes, suggested by @j-f1 Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com>
This commit is contained in:
parent
304a384b6c
commit
e436c01430
6 changed files with 195 additions and 49 deletions
|
@ -27,6 +27,7 @@ import { usePageType } from '../Page';
|
||||||
import { Page } from '../Page';
|
import { Page } from '../Page';
|
||||||
import { OpSelect } from '../modals/EditRule';
|
import { OpSelect } from '../modals/EditRule';
|
||||||
import { AmountInput, BetweenAmountInput } from '../util/AmountInput';
|
import { AmountInput, BetweenAmountInput } from '../util/AmountInput';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
function mergeFields(defaults, initial) {
|
function mergeFields(defaults, initial) {
|
||||||
let res = { ...defaults };
|
let res = { ...defaults };
|
||||||
|
@ -92,6 +93,7 @@ export default function ScheduleDetails() {
|
||||||
});
|
});
|
||||||
|
|
||||||
let pageType = usePageType();
|
let pageType = usePageType();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
let [state, dispatch] = useReducer(
|
let [state, dispatch] = useReducer(
|
||||||
(state, action) => {
|
(state, action) => {
|
||||||
|
@ -365,8 +367,10 @@ export default function ScheduleDetails() {
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: 'form-error',
|
type: 'form-error',
|
||||||
error:
|
// Note: email is outside of translation to be easily replace on future
|
||||||
'An error occurred while saving. Please contact help@actualbudget.com for support.'
|
error: t('support.anErrorOccuredWhileSaving', {
|
||||||
|
email: 'help@actualbudget.com'
|
||||||
|
})
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (adding) {
|
if (adding) {
|
||||||
|
@ -423,15 +427,19 @@ export default function ScheduleDetails() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page
|
<Page
|
||||||
title={payee ? `Schedule: ${payee.name}` : 'Schedule'}
|
title={
|
||||||
|
payee
|
||||||
|
? t('schedules.scheduleNamed', { name: payee.name })
|
||||||
|
: t('general.schedule')
|
||||||
|
}
|
||||||
modalSize="medium"
|
modalSize="medium"
|
||||||
>
|
>
|
||||||
<Stack direction="row" style={{ marginTop: 20 }}>
|
<Stack direction="row" style={{ marginTop: 20 }}>
|
||||||
<FormField style={{ flex: 1 }}>
|
<FormField style={{ flex: 1 }}>
|
||||||
<FormLabel title="Payee" />
|
<FormLabel title={t('general.payee')} />
|
||||||
<PayeeAutocomplete
|
<PayeeAutocomplete
|
||||||
value={state.fields.payee}
|
value={state.fields.payee}
|
||||||
inputProps={{ placeholder: '(none)' }}
|
inputProps={{ placeholder: t('schedules.none') }}
|
||||||
onSelect={id =>
|
onSelect={id =>
|
||||||
dispatch({ type: 'set-field', field: 'payee', value: id })
|
dispatch({ type: 'set-field', field: 'payee', value: id })
|
||||||
}
|
}
|
||||||
|
@ -439,10 +447,10 @@ export default function ScheduleDetails() {
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField style={{ flex: 1 }}>
|
<FormField style={{ flex: 1 }}>
|
||||||
<FormLabel title="Account" />
|
<FormLabel title={t('general.account')} />
|
||||||
<AccountAutocomplete
|
<AccountAutocomplete
|
||||||
value={state.fields.account}
|
value={state.fields.account}
|
||||||
inputProps={{ placeholder: '(none)' }}
|
inputProps={{ placeholder: t('schedules.none') }}
|
||||||
onSelect={id =>
|
onSelect={id =>
|
||||||
dispatch({ type: 'set-field', field: 'account', value: id })
|
dispatch({ type: 'set-field', field: 'account', value: id })
|
||||||
}
|
}
|
||||||
|
@ -451,18 +459,21 @@ export default function ScheduleDetails() {
|
||||||
|
|
||||||
<FormField style={{ flex: 1 }}>
|
<FormField style={{ flex: 1 }}>
|
||||||
<Stack direction="row" align="center" style={{ marginBottom: 3 }}>
|
<Stack direction="row" align="center" style={{ marginBottom: 3 }}>
|
||||||
<FormLabel title="Amount" style={{ margin: 0, flex: 1 }} />
|
<FormLabel
|
||||||
|
title={t('general.amount')}
|
||||||
|
style={{ margin: 0, flex: 1 }}
|
||||||
|
/>
|
||||||
<OpSelect
|
<OpSelect
|
||||||
ops={['is', 'isapprox', 'isbetween']}
|
ops={['is', 'isapprox', 'isbetween']}
|
||||||
value={state.fields.amountOp}
|
value={state.fields.amountOp}
|
||||||
formatOp={op => {
|
formatOp={op => {
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case 'is':
|
case 'is':
|
||||||
return 'is exactly';
|
return t('schedules.isExactly');
|
||||||
case 'isapprox':
|
case 'isapprox':
|
||||||
return 'is approximately';
|
return t('schedules.isApproximately');
|
||||||
case 'isbetween':
|
case 'isbetween':
|
||||||
return 'is between';
|
return t('schedules.isBetween');
|
||||||
default:
|
default:
|
||||||
throw new Error('Invalid op for select: ' + op);
|
throw new Error('Invalid op for select: ' + op);
|
||||||
}
|
}
|
||||||
|
@ -504,7 +515,7 @@ export default function ScheduleDetails() {
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<View style={{ marginTop: 20 }}>
|
<View style={{ marginTop: 20 }}>
|
||||||
<FormLabel title="Date" />
|
<FormLabel title={t('general.date')} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Stack direction="row" align="flex-start">
|
<Stack direction="row" align="flex-start">
|
||||||
|
@ -529,7 +540,7 @@ export default function ScheduleDetails() {
|
||||||
{state.upcomingDates && (
|
{state.upcomingDates && (
|
||||||
<View style={{ fontSize: 13, marginTop: 20 }}>
|
<View style={{ fontSize: 13, marginTop: 20 }}>
|
||||||
<Text style={{ color: colors.n4, fontWeight: 600 }}>
|
<Text style={{ color: colors.n4, fontWeight: 600 }}>
|
||||||
Upcoming dates
|
{t('schedules.upcomingDates')}
|
||||||
</Text>
|
</Text>
|
||||||
<Stack
|
<Stack
|
||||||
direction="column"
|
direction="column"
|
||||||
|
@ -561,7 +572,7 @@ export default function ScheduleDetails() {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<label for="form_repeats" style={{ userSelect: 'none' }}>
|
<label for="form_repeats" style={{ userSelect: 'none' }}>
|
||||||
Repeats
|
{t('general.repeats')}
|
||||||
</label>
|
</label>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
@ -592,7 +603,7 @@ export default function ScheduleDetails() {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<label for="form_posts_transaction" style={{ userSelect: 'none' }}>
|
<label for="form_posts_transaction" style={{ userSelect: 'none' }}>
|
||||||
Automatically add transaction
|
{t('schedules.automaticallyAddTransaction')}
|
||||||
</label>
|
</label>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
@ -606,8 +617,7 @@ export default function ScheduleDetails() {
|
||||||
lineHeight: '1.4em'
|
lineHeight: '1.4em'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
If checked, the schedule will automatically create transactions for
|
{t('schedules.automaticallyAddTransactionAdvice')}
|
||||||
you in the specified account
|
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{!adding && state.schedule.rule && (
|
{!adding && state.schedule.rule && (
|
||||||
|
@ -621,11 +631,11 @@ export default function ScheduleDetails() {
|
||||||
width: 350
|
width: 350
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
This schedule has custom conditions and actions
|
{t('schedules.thisScheduleHasCustomConditionsAndActions')}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
<Button onClick={() => onEditRule()} disabled={adding}>
|
<Button onClick={() => onEditRule()} disabled={adding}>
|
||||||
Edit as rule
|
{t('schedules.editAsRule')}
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
@ -637,11 +647,11 @@ export default function ScheduleDetails() {
|
||||||
{adding ? (
|
{adding ? (
|
||||||
<View style={{ flexDirection: 'row', padding: '5px 0' }}>
|
<View style={{ flexDirection: 'row', padding: '5px 0' }}>
|
||||||
<Text style={{ color: colors.n4 }}>
|
<Text style={{ color: colors.n4 }}>
|
||||||
These transactions match this schedule:
|
{t('schedules.theseTransactionsMatchThisSchedule')}
|
||||||
</Text>
|
</Text>
|
||||||
<View style={{ flex: 1 }} />
|
<View style={{ flex: 1 }} />
|
||||||
<Text style={{ color: colors.n6 }}>
|
<Text style={{ color: colors.n6 }}>
|
||||||
Select transactions to link on save
|
{t('schedules.selectTransactionsToLinkOnSave')}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
|
@ -656,7 +666,7 @@ export default function ScheduleDetails() {
|
||||||
}}
|
}}
|
||||||
onClick={() => onSwitchTransactions('linked')}
|
onClick={() => onSwitchTransactions('linked')}
|
||||||
>
|
>
|
||||||
Linked transactions
|
{t('schedules.linkedTransactions')}
|
||||||
</Button>{' '}
|
</Button>{' '}
|
||||||
<Button
|
<Button
|
||||||
bare
|
bare
|
||||||
|
@ -669,15 +679,20 @@ export default function ScheduleDetails() {
|
||||||
}}
|
}}
|
||||||
onClick={() => onSwitchTransactions('matched')}
|
onClick={() => onSwitchTransactions('matched')}
|
||||||
>
|
>
|
||||||
Find matching transactions
|
{t('schedules.findMatchingTransactions')}
|
||||||
</Button>
|
</Button>
|
||||||
<View style={{ flex: 1 }} />
|
<View style={{ flex: 1 }} />
|
||||||
<SelectedItemsButton
|
<SelectedItemsButton
|
||||||
name="transactions"
|
name="transactions"
|
||||||
items={
|
items={
|
||||||
state.transactionsMode === 'linked'
|
state.transactionsMode === 'linked'
|
||||||
? [{ name: 'unlink', text: 'Unlink from schedule' }]
|
? [
|
||||||
: [{ name: 'link', text: 'Link to schedule' }]
|
{
|
||||||
|
name: 'unlink',
|
||||||
|
text: t('schedules.unlinkFromSchedule')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: [{ name: 'link', text: t('schedules.linkToSchedule') }]
|
||||||
}
|
}
|
||||||
onSelect={(name, ids) => {
|
onSelect={(name, ids) => {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
|
@ -715,10 +730,10 @@ export default function ScheduleDetails() {
|
||||||
>
|
>
|
||||||
{state.error && <Text style={{ color: colors.r4 }}>{state.error}</Text>}
|
{state.error && <Text style={{ color: colors.r4 }}>{state.error}</Text>}
|
||||||
<Button style={{ marginRight: 10 }} onClick={() => history.goBack()}>
|
<Button style={{ marginRight: 10 }} onClick={() => history.goBack()}>
|
||||||
Cancel
|
{t('general.cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button primary onClick={onSave}>
|
<Button primary onClick={onSave}>
|
||||||
{adding ? 'Add' : 'Save'}
|
{adding ? t('general.add') : t('general.save')}
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Page>
|
</Page>
|
||||||
|
|
|
@ -22,12 +22,15 @@ import DotsHorizontalTriple from 'loot-design/src/svg/v1/DotsHorizontalTriple';
|
||||||
import Check from 'loot-design/src/svg/v2/Check';
|
import Check from 'loot-design/src/svg/v2/Check';
|
||||||
import DisplayId from '../util/DisplayId';
|
import DisplayId from '../util/DisplayId';
|
||||||
import { StatusBadge } from './StatusBadge';
|
import { StatusBadge } from './StatusBadge';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
export let ROW_HEIGHT = 43;
|
export let ROW_HEIGHT = 43;
|
||||||
|
|
||||||
function OverflowMenu({ schedule, status, onAction }) {
|
function OverflowMenu({ schedule, status, onAction }) {
|
||||||
let [open, setOpen] = useState(false);
|
let [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<Button
|
<Button
|
||||||
|
@ -58,15 +61,15 @@ function OverflowMenu({ schedule, status, onAction }) {
|
||||||
items={[
|
items={[
|
||||||
status === 'due' && {
|
status === 'due' && {
|
||||||
name: 'post-transaction',
|
name: 'post-transaction',
|
||||||
text: 'Post transaction'
|
text: t('schedules.postTransaction')
|
||||||
},
|
},
|
||||||
...(schedule.completed
|
...(schedule.completed
|
||||||
? [{ name: 'restart', text: 'Restart' }]
|
? [{ name: 'restart', text: t('general.restart') }]
|
||||||
: [
|
: [
|
||||||
{ name: 'skip', text: 'Skip next date' },
|
{ name: 'skip', text: t('schedules.skipNextDate') },
|
||||||
{ name: 'complete', text: 'Complete' }
|
{ name: 'complete', text: t('general.complete') }
|
||||||
]),
|
]),
|
||||||
{ name: 'delete', text: 'Delete' }
|
{ name: 'delete', text: t('general.delete') }
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
@ -80,6 +83,8 @@ export function ScheduleAmountCell({ amount, op }) {
|
||||||
let str = integerToCurrency(Math.abs(num || 0));
|
let str = integerToCurrency(Math.abs(num || 0));
|
||||||
let isApprox = op === 'isapprox' || op === 'isbetween';
|
let isApprox = op === 'isapprox' || op === 'isbetween';
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Cell
|
<Cell
|
||||||
width={100}
|
width={100}
|
||||||
|
@ -99,7 +104,7 @@ export function ScheduleAmountCell({ amount, op }) {
|
||||||
lineHeight: '1em',
|
lineHeight: '1em',
|
||||||
marginRight: 10
|
marginRight: 10
|
||||||
}}
|
}}
|
||||||
title={(isApprox ? 'Approximately ' : '') + str}
|
title={t('general.approximatelyWithAmount', { amount: str })}
|
||||||
>
|
>
|
||||||
~
|
~
|
||||||
</View>
|
</View>
|
||||||
|
@ -112,7 +117,9 @@ export function ScheduleAmountCell({ amount, op }) {
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
textOverflow: 'ellipsis'
|
textOverflow: 'ellipsis'
|
||||||
}}
|
}}
|
||||||
title={(isApprox ? 'Approximately ' : '') + str}
|
title={
|
||||||
|
isApprox ? t('general.approximatelyWithAmount', { amount: str }) : str
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{num > 0 ? `+${str}` : `${str}`}
|
{num > 0 ? `+${str}` : `${str}`}
|
||||||
</Text>
|
</Text>
|
||||||
|
@ -135,6 +142,8 @@ export function SchedulesTable({
|
||||||
|
|
||||||
let [showCompleted, setShowCompleted] = useState(false);
|
let [showCompleted, setShowCompleted] = useState(false);
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
let items = useMemo(() => {
|
let items = useMemo(() => {
|
||||||
if (!allowCompleted) {
|
if (!allowCompleted) {
|
||||||
return schedules.filter(s => !s.completed);
|
return schedules.filter(s => !s.completed);
|
||||||
|
@ -219,7 +228,7 @@ export function SchedulesTable({
|
||||||
color: colors.n6
|
color: colors.n6
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Show completed schedules
|
{t('schedules.showCompletedSchedules')}
|
||||||
</Field>
|
</Field>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
|
@ -230,16 +239,16 @@ export function SchedulesTable({
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TableHeader height={ROW_HEIGHT} inset={15} version="v2">
|
<TableHeader height={ROW_HEIGHT} inset={15} version="v2">
|
||||||
<Field width="flex">Payee</Field>
|
<Field width="flex">{t('general.payee')}</Field>
|
||||||
<Field width="flex">Account</Field>
|
<Field width="flex">{t('general.account')}</Field>
|
||||||
<Field width={110}>Next date</Field>
|
<Field width={110}>{t('schedules.nextDate')}</Field>
|
||||||
<Field width={120}>Status</Field>
|
<Field width={120}>{t('general.status')}</Field>
|
||||||
<Field width={100} style={{ textAlign: 'right' }}>
|
<Field width={100} style={{ textAlign: 'right' }}>
|
||||||
Amount
|
{t('general.amount')}
|
||||||
</Field>
|
</Field>
|
||||||
{!minimal && (
|
{!minimal && (
|
||||||
<Field width={80} style={{ textAlign: 'center' }}>
|
<Field width={80} style={{ textAlign: 'center' }}>
|
||||||
Recurring
|
{t('general.recurring')}
|
||||||
</Field>
|
</Field>
|
||||||
)}
|
)}
|
||||||
{!minimal && <Field width={40}></Field>}
|
{!minimal && <Field width={40}></Field>}
|
||||||
|
@ -251,7 +260,7 @@ export function SchedulesTable({
|
||||||
style={[{ flex: 1, backgroundColor: 'transparent' }, style]}
|
style={[{ flex: 1, backgroundColor: 'transparent' }, style]}
|
||||||
items={items}
|
items={items}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
renderEmpty="No schedules"
|
renderEmpty={t('schedules.noSchedules')}
|
||||||
allowPopupsEscape={items.length < 6}
|
allowPopupsEscape={items.length < 6}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -9,54 +9,65 @@ import CalendarIcon from 'loot-design/src/svg/v2/Calendar';
|
||||||
import ValidationCheck from 'loot-design/src/svg/v2/ValidationCheck';
|
import ValidationCheck from 'loot-design/src/svg/v2/ValidationCheck';
|
||||||
import FavoriteStar from 'loot-design/src/svg/v2/FavoriteStar';
|
import FavoriteStar from 'loot-design/src/svg/v2/FavoriteStar';
|
||||||
import CheckCircle1 from 'loot-design/src/svg/v2/CheckCircle1';
|
import CheckCircle1 from 'loot-design/src/svg/v2/CheckCircle1';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
export function getStatusProps(status) {
|
export function getStatusProps(status) {
|
||||||
let color, backgroundColor, Icon;
|
let color, backgroundColor, Icon, title;
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'missed':
|
case 'missed':
|
||||||
color = colors.r1;
|
color = colors.r1;
|
||||||
backgroundColor = colors.r10;
|
backgroundColor = colors.r10;
|
||||||
Icon = EditSkull1;
|
Icon = EditSkull1;
|
||||||
|
title = t('status.missed');
|
||||||
break;
|
break;
|
||||||
case 'due':
|
case 'due':
|
||||||
color = colors.y1;
|
color = colors.y1;
|
||||||
backgroundColor = colors.y9;
|
backgroundColor = colors.y9;
|
||||||
Icon = AlertTriangle;
|
Icon = AlertTriangle;
|
||||||
|
title = t('status.due');
|
||||||
break;
|
break;
|
||||||
case 'upcoming':
|
case 'upcoming':
|
||||||
color = colors.p1;
|
color = colors.p1;
|
||||||
backgroundColor = colors.p10;
|
backgroundColor = colors.p10;
|
||||||
Icon = CalendarIcon;
|
Icon = CalendarIcon;
|
||||||
|
title = t('status.upcoming');
|
||||||
break;
|
break;
|
||||||
case 'paid':
|
case 'paid':
|
||||||
color = colors.g2;
|
color = colors.g2;
|
||||||
backgroundColor = colors.g10;
|
backgroundColor = colors.g10;
|
||||||
Icon = ValidationCheck;
|
Icon = ValidationCheck;
|
||||||
|
title = t('status.paid');
|
||||||
break;
|
break;
|
||||||
case 'completed':
|
case 'completed':
|
||||||
color = colors.n4;
|
color = colors.n4;
|
||||||
backgroundColor = colors.n11;
|
backgroundColor = colors.n11;
|
||||||
Icon = FavoriteStar;
|
Icon = FavoriteStar;
|
||||||
|
title = t('status.completed');
|
||||||
break;
|
break;
|
||||||
case 'pending':
|
case 'pending':
|
||||||
color = colors.g4;
|
color = colors.g4;
|
||||||
backgroundColor = colors.g11;
|
backgroundColor = colors.g11;
|
||||||
Icon = CalendarIcon;
|
Icon = CalendarIcon;
|
||||||
|
title = t('status.pending');
|
||||||
break;
|
break;
|
||||||
case 'scheduled':
|
case 'scheduled':
|
||||||
color = colors.n1;
|
color = colors.n1;
|
||||||
backgroundColor = colors.n11;
|
backgroundColor = colors.n11;
|
||||||
Icon = CalendarIcon;
|
Icon = CalendarIcon;
|
||||||
|
title = t('status.scheduled');
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
color = colors.n1;
|
color = colors.n1;
|
||||||
backgroundColor = colors.n11;
|
backgroundColor = colors.n11;
|
||||||
Icon = CheckCircle1;
|
Icon = CheckCircle1;
|
||||||
|
title = status;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { color, backgroundColor, Icon };
|
return { title, color, backgroundColor, Icon };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function StatusIcon({ status }) {
|
export function StatusIcon({ status }) {
|
||||||
|
@ -66,7 +77,7 @@ export function StatusIcon({ status }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function StatusBadge({ status, style }) {
|
export function StatusBadge({ status, style }) {
|
||||||
let { color, backgroundColor, Icon } = getStatusProps(status);
|
let { title, color, backgroundColor, Icon } = getStatusProps(status);
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
|
@ -90,7 +101,7 @@ export function StatusBadge({ status, style }) {
|
||||||
marginRight: 7
|
marginRight: 7
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Text style={{ lineHeight: '1em' }}>{titleFirst(status)}</Text>
|
<Text style={{ lineHeight: '1em' }}>{titleFirst(title)}</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,15 @@ import { send } from 'loot-core/src/platform/client/fetch';
|
||||||
import { useSchedules } from 'loot-core/src/client/data-hooks/schedules';
|
import { useSchedules } from 'loot-core/src/client/data-hooks/schedules';
|
||||||
import { Page } from '../Page';
|
import { Page } from '../Page';
|
||||||
import { SchedulesTable, ROW_HEIGHT } from './SchedulesTable';
|
import { SchedulesTable, ROW_HEIGHT } from './SchedulesTable';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
export default function Schedules() {
|
export default function Schedules() {
|
||||||
let history = useHistory();
|
let history = useHistory();
|
||||||
|
|
||||||
let scheduleData = useSchedules();
|
let scheduleData = useSchedules();
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
if (scheduleData == null) {
|
if (scheduleData == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -50,7 +53,7 @@ export default function Schedules() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page title="Schedules">
|
<Page title={t('general.schedules')}>
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
marginTop: 20,
|
marginTop: 20,
|
||||||
|
@ -70,7 +73,7 @@ export default function Schedules() {
|
||||||
|
|
||||||
<View style={{ alignItems: 'flex-end', margin: '20px 0', flexShrink: 0 }}>
|
<View style={{ alignItems: 'flex-end', margin: '20px 0', flexShrink: 0 }}>
|
||||||
<Button primary onClick={onAdd}>
|
<Button primary onClick={onAdd}>
|
||||||
Add new schedule
|
{t('schedules.addNewSchedule')}
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
</Page>
|
</Page>
|
||||||
|
|
|
@ -8,8 +8,62 @@
|
||||||
"needAccountMessage": "For Actual to be useful, you need to <strong>add an account</strong>. You can link an account to automatically download transactions, or manage it locally yourself."
|
"needAccountMessage": "For Actual to be useful, you need to <strong>add an account</strong>. You can link an account to automatically download transactions, or manage it locally yourself."
|
||||||
},
|
},
|
||||||
"bootstrap": {
|
"bootstrap": {
|
||||||
"title": "Bootstrap this Actual instance",
|
|
||||||
"setPassword": "Set a password for this server instance",
|
"setPassword": "Set a password for this server instance",
|
||||||
|
"title": "Bootstrap this Actual instance",
|
||||||
"tryDemo": "Try Demo"
|
"tryDemo": "Try Demo"
|
||||||
|
},
|
||||||
|
"general": {
|
||||||
|
"account": "Account",
|
||||||
|
"add": "Add",
|
||||||
|
"amount": "Amount",
|
||||||
|
"approximatelyWithAmount": "Approximately {{amount}}",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"complete": "Complete",
|
||||||
|
"date": "Date",
|
||||||
|
"delete": "Delete",
|
||||||
|
"payee": "Payee",
|
||||||
|
"recurring": "Recurring",
|
||||||
|
"repeats": "Repeats",
|
||||||
|
"restart": "Restart",
|
||||||
|
"save": "Save",
|
||||||
|
"schedule": "Schedule",
|
||||||
|
"schedules": "Schedules",
|
||||||
|
"status": "Status"
|
||||||
|
},
|
||||||
|
"schedules": {
|
||||||
|
"addNewSchedule": "Add new schedule",
|
||||||
|
"automaticallyAddTransaction": "Automatically add transaction",
|
||||||
|
"automaticallyAddTransactionAdvice": "If checked, the schedule will automatically create transactions for you in the specified account",
|
||||||
|
"editAsRule": "Edit as rule",
|
||||||
|
"findMatchingTransactions": "Find matching transactions",
|
||||||
|
"isApproximately": "is approximately",
|
||||||
|
"isBetween": "is between",
|
||||||
|
"isExactly": "is exactly",
|
||||||
|
"linkedTransactions": "Linked transactions",
|
||||||
|
"linkToSchedule": "Link to schedule",
|
||||||
|
"nextDate": "Next date",
|
||||||
|
"none": "(none)",
|
||||||
|
"noSchedules": "No schedules",
|
||||||
|
"postTransaction": "Post transaction",
|
||||||
|
"scheduleNamed": "Schedule: {{name}}",
|
||||||
|
"selectTransactionsToLinkOnSave": "Select transactions to link on save",
|
||||||
|
"showCompletedSchedules": "Show completed schedules",
|
||||||
|
"skipNextDate": "Skip next date",
|
||||||
|
"theseTransactionsMatchThisSchedule": "These transactions match this schedule:",
|
||||||
|
"thisScheduleHasCustomConditionsAndActions": "This schedule has custom conditions and actions",
|
||||||
|
"unlinkFromSchedule": "Unlink from schedule",
|
||||||
|
"upcomingDates": "Upcoming dates"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"completed": "completed",
|
||||||
|
"due": "due",
|
||||||
|
"missed": "missed",
|
||||||
|
"paid": "paid",
|
||||||
|
"pending": "pending",
|
||||||
|
"scheduled": "scheduled",
|
||||||
|
"upcoming": "upcoming"
|
||||||
|
},
|
||||||
|
"support": {
|
||||||
|
"anErrorOccuredWhileSaving": "An error occurred while saving. Please contact {{email}} for support."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,5 +11,59 @@
|
||||||
"setPassword": "Establecer una contraseña para esta instancia de servidor",
|
"setPassword": "Establecer una contraseña para esta instancia de servidor",
|
||||||
"title": "Bootstrap esta instancia de Actual",
|
"title": "Bootstrap esta instancia de Actual",
|
||||||
"tryDemo": "Probar Demo"
|
"tryDemo": "Probar Demo"
|
||||||
|
},
|
||||||
|
"general": {
|
||||||
|
"account": "Cuenta",
|
||||||
|
"add": "Agregar",
|
||||||
|
"amount": "Monto",
|
||||||
|
"approximatelyWithAmount": "Aproximadamente {{amount}}",
|
||||||
|
"cancel": "Cancelar",
|
||||||
|
"complete": "Completar",
|
||||||
|
"date": "Fecha",
|
||||||
|
"delete": "Borrar",
|
||||||
|
"payee": "Beneficiario",
|
||||||
|
"recurring": "Periódico",
|
||||||
|
"repeats": "Repetir",
|
||||||
|
"restart": "Reiniciar",
|
||||||
|
"save": "Guardar",
|
||||||
|
"schedule": "Agenda",
|
||||||
|
"schedules": "Agendas",
|
||||||
|
"status": "Estado"
|
||||||
|
},
|
||||||
|
"schedules": {
|
||||||
|
"addNewSchedule": "Agregar nuevo agenda",
|
||||||
|
"automaticallyAddTransaction": "Agregar transacción automáticamente",
|
||||||
|
"automaticallyAddTransactionAdvice": "Si se selecciona, la agenda creará automáticamente una transacción para la cuenta especificada",
|
||||||
|
"editAsRule": "Editar como regla",
|
||||||
|
"findMatchingTransactions": "Encontrar transacciones que coincidan",
|
||||||
|
"isApproximately": "es aproximadamente",
|
||||||
|
"isBetween": "está entre",
|
||||||
|
"isExactly": "es exactamente",
|
||||||
|
"linkedTransactions": "Transacciones vinculadas",
|
||||||
|
"linkToSchedule": "Vincular a agenda",
|
||||||
|
"nextDate": "Próxima fecha",
|
||||||
|
"none": "(ninguno)",
|
||||||
|
"noSchedules": "Sin agendas",
|
||||||
|
"postTransaction": "Publicar transacción",
|
||||||
|
"scheduleNamed": "Agenda: {{name}}",
|
||||||
|
"selectTransactionsToLinkOnSave": "Seleccionar transacciones para vincular al guardar",
|
||||||
|
"showCompletedSchedules": "Mostrar agendas completadas",
|
||||||
|
"skipNextDate": "Saltar próxima fecha",
|
||||||
|
"theseTransactionsMatchThisSchedule": "Éstas transacciones coinciden con la agenda",
|
||||||
|
"thisScheduleHasCustomConditionsAndActions": "Ésta agenda tiene condiciones y acciones personalizadas",
|
||||||
|
"unlinkFromSchedule": "Desvincular de la agenda",
|
||||||
|
"upcomingDates": "Próximas fechas"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"completed": "completo",
|
||||||
|
"due": "adeudado",
|
||||||
|
"missed": "omitido",
|
||||||
|
"paid": "pago",
|
||||||
|
"pending": "pendiente",
|
||||||
|
"scheduled": "agendado",
|
||||||
|
"upcoming": "próximo"
|
||||||
|
},
|
||||||
|
"support": {
|
||||||
|
"anErrorOccuredWhileSaving": "Ocurrió un error al guardar. Por favor, póngase en contacto con {{email}} para obtener asistencia."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue