Skip to content

Commit

Permalink
Move RecurringSchedulePicker.jsx -> RecurringSchedulePicker.tsx
Browse files Browse the repository at this point in the history
  • Loading branch information
jfdoming committed Sep 8, 2024
1 parent 9a61972 commit 35b459d
Showing 1 changed file with 120 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import React, { useEffect, useReducer, useRef, useState } from 'react';
import React, {

Check warning on line 1 in packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx

View workflow job for this annotation

GitHub Actions / lint

Imports "React" and "Dispatch" are only used as type
Dispatch,
useEffect,
useReducer,
useRef,
useState,
} from 'react';

import { sendCatch } from 'loot-core/src/platform/client/fetch';
import * as monthUtils from 'loot-core/src/shared/months';
Expand All @@ -19,6 +25,8 @@ import { View } from '../common/View';
import { Checkbox } from '../forms';

import { DateSelect } from './DateSelect';

Check warning on line 27 in packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx

View workflow job for this annotation

GitHub Actions / lint

There should be at least one empty line between import groups
import { RecurConfig, RecurPattern } from 'loot-core/types/models';

Check warning on line 28 in packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx

View workflow job for this annotation

GitHub Actions / lint

All imports in the declaration are only used as types. Use `import type`

Check warning on line 28 in packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx

View workflow job for this annotation

GitHub Actions / lint

`loot-core/types/models` import should occur before import of `../../hooks/useDateFormat`
import { WithRequired } from 'loot-core/types/util';

Check warning on line 29 in packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx

View workflow job for this annotation

GitHub Actions / lint

All imports in the declaration are only used as types. Use `import type`

Check warning on line 29 in packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx

View workflow job for this annotation

GitHub Actions / lint

`loot-core/types/util` import should occur before import of `../../hooks/useDateFormat`

// ex: There is no 6th Friday of the Month
const MAX_DAY_OF_WEEK_INTERVAL = 5;
Expand All @@ -28,7 +36,7 @@ const FREQUENCY_OPTIONS = [
{ id: 'weekly', name: 'Weeks' },
{ id: 'monthly', name: 'Months' },
{ id: 'yearly', name: 'Years' },
];
] as const;

const DAY_OF_MONTH_OPTIONS = [...Array(31).keys()].map(day => day + 1);

Expand All @@ -40,16 +48,16 @@ const DAY_OF_WEEK_OPTIONS = [
{ id: 'TH', name: 'Thursday' },
{ id: 'FR', name: 'Friday' },
{ id: 'SA', name: 'Saturday' },
];
] as const;

function parsePatternValue(value) {
function parsePatternValue(value: string | number) {
if (value === 'last') {
return -1;
}
return Number(value);
}

function parseConfig(config) {
function parseConfig(config: Partial<RecurConfig>): StateConfig {
return {
start: monthUtils.currentDay(),
interval: 1,
Expand All @@ -58,28 +66,37 @@ function parseConfig(config) {
skipWeekend: false,
weekendSolveMode: 'before',
endMode: 'never',
endOccurrences: '1',
endOccurrences: 1,
endDate: monthUtils.currentDay(),
...config,
};
}

function unparseConfig(parsed) {
function unparseConfig(parsed: StateConfig): RecurConfig {
return {
...parsed,
interval: validInterval(parsed.interval),
endOccurrences: validInterval(parsed.endOccurrences),
};
}

function createMonthlyRecurrence(startDate) {
function createMonthlyRecurrence(startDate: string) {
return {
value: parseInt(monthUtils.format(startDate, 'd')),
type: 'day',
type: 'day' as const,
};
}

function boundedRecurrence({ field, value, recurrence }) {
function boundedRecurrence({
field,
value,
recurrence,
}: {
recurrence: RecurPattern;
} & (
| { field: 'type'; value: RecurPattern['type'] }
| { field: 'value'; value: RecurPattern['value'] }
)) {
if (
(field === 'value' &&
recurrence.type !== 'day' &&
Expand All @@ -93,7 +110,48 @@ function boundedRecurrence({ field, value, recurrence }) {
return { [field]: value };
}

function reducer(state, action) {
type StateConfig = Omit<
WithRequired<RecurConfig, 'patterns' | 'endDate' | 'weekendSolveMode'>,
'interval' | 'endOccurrences'
> & {
interval: number | string;
endOccurrences: number | string;
};

type ReducerState = {
config: StateConfig;
};

type UpdateRecurrenceAction =
| {
type: 'update-recurrence';
recurrence: RecurPattern;
field: 'type';
value: RecurPattern['type'];
}
| {
type: 'update-recurrence';
recurrence: RecurPattern;
field: 'value';
value: RecurPattern['value'];
};

type ChangeFieldAction<T extends keyof StateConfig> = {
type: 'change-field';
field: T;
value: StateConfig[T];
};

type ReducerAction =
| { type: 'replace-config'; config: StateConfig }
| ChangeFieldAction<keyof StateConfig>
| UpdateRecurrenceAction
| { type: 'add-recurrence' }
| { type: 'remove-recurrence'; recurrence: any }

Check failure on line 150 in packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
| { type: 'set-skip-weekend'; skipWeekend: boolean }
| { type: 'set-weekend-solve'; value: StateConfig['weekendSolveMode'] };

function reducer(state: ReducerState, action: ReducerAction): ReducerState {
switch (action.type) {
case 'replace-config':
return { ...state, config: action.config };
Expand Down Expand Up @@ -159,7 +217,7 @@ function reducer(state, action) {
}
}

function SchedulePreview({ previewDates }) {
function SchedulePreview({ previewDates }: { previewDates: Date[] }) {
const dateFormat = (useDateFormat() || 'MM/dd/yyyy')
.replace('MM', 'M')
.replace('dd', 'd');
Expand Down Expand Up @@ -198,15 +256,18 @@ function SchedulePreview({ previewDates }) {
);
}

function validInterval(interval) {
const intInterval = parseInt(interval);
function validInterval(interval: string | number) {
const intInterval = Number(interval);
return Number.isInteger(intInterval) && intInterval > 0 ? intInterval : 1;
}

function MonthlyPatterns({ config, dispatch }) {
const updateRecurrence = (recurrence, field, value) =>
dispatch({ type: 'update-recurrence', recurrence, field, value });

function MonthlyPatterns({
config,
dispatch,
}: {
config: StateConfig;
dispatch: Dispatch<ReducerAction>;
}) {
return (
<Stack spacing={2} style={{ marginTop: 10 }}>
{config.patterns.map((recurrence, idx) => (
Expand All @@ -221,24 +282,34 @@ function MonthlyPatterns({ config, dispatch }) {
options={[
[-1, 'Last'],
Menu.line,
...DAY_OF_MONTH_OPTIONS.map(opt => [opt, opt]),
...DAY_OF_MONTH_OPTIONS.map(opt => [opt, String(opt)] as const),
]}
value={recurrence.value}
onChange={value =>
updateRecurrence(recurrence, 'value', parsePatternValue(value))
dispatch({
type: 'update-recurrence',
recurrence,
field: 'value',
value: parsePatternValue(value),
})
}
disabledKeys={['-']}
buttonStyle={{ flex: 1, marginRight: 10 }}
/>
<Select
options={[
['day', 'Day'],
Menu.line,
...DAY_OF_WEEK_OPTIONS.map(opt => [opt.id, opt.name]),
...DAY_OF_WEEK_OPTIONS.map(opt => [opt.id, opt.name] as const),
]}
value={recurrence.type}
onChange={value => updateRecurrence(recurrence, 'type', value)}
disabledKeys={['-']}
onChange={value => {
dispatch({
type: 'update-recurrence',
recurrence,
field: 'type',
value,
});
}}
buttonStyle={{ flex: 1, marginRight: 10 }}
/>
<Button
Expand Down Expand Up @@ -268,7 +339,15 @@ function MonthlyPatterns({ config, dispatch }) {
);
}

function RecurringScheduleTooltip({ config: currentConfig, onClose, onSave }) {
function RecurringScheduleTooltip({
config: currentConfig,
onClose,
onSave,
}: {
config: RecurConfig;
onClose: () => void;
onSave: (config: RecurConfig) => void;
}) {
const [previewDates, setPreviewDates] = useState(null);

const [state, dispatch] = useReducer(reducer, {
Expand All @@ -289,8 +368,10 @@ function RecurringScheduleTooltip({ config: currentConfig, onClose, onSave }) {

const { config } = state;

const updateField = (field, value) =>
dispatch({ type: 'change-field', field, value });
const updateField = <Field extends keyof RecurConfig>(
field: Field,
value: StateConfig[Field],
) => dispatch({ type: 'change-field', field, value });

useEffect(() => {
async function run() {
Expand Down Expand Up @@ -382,7 +463,6 @@ function RecurringScheduleTooltip({ config: currentConfig, onClose, onSave }) {
<Button
style={{
backgroundColor: theme.tableBackground,
':hover': { backgroundColor: theme.tableBackground },
}}
onPress={() => dispatch({ type: 'add-recurrence' })}
>
Expand Down Expand Up @@ -460,12 +540,22 @@ function RecurringScheduleTooltip({ config: currentConfig, onClose, onSave }) {
);
}

export function RecurringSchedulePicker({ value, buttonStyle, onChange }) {
type RecurringSchedulePickerProps = {
value: RecurConfig;
buttonStyle?: React.CSSProperties;

Check warning on line 545 in packages/desktop-client/src/components/select/RecurringSchedulePicker.tsx

View workflow job for this annotation

GitHub Actions / lint

Using default React import is discouraged, please use named exports directly instead
onChange: (config: RecurConfig) => void;
};

export function RecurringSchedulePicker({
value,
buttonStyle,
onChange,
}: RecurringSchedulePickerProps) {
const triggerRef = useRef(null);
const [isOpen, setIsOpen] = useState(false);
const dateFormat = useDateFormat() || 'MM/dd/yyyy';

function onSave(config) {
function onSave(config: RecurConfig) {
onChange(config);
setIsOpen(false);
}
Expand Down

0 comments on commit 35b459d

Please sign in to comment.