Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { HourglassDisabled } from '@mui/icons-material';
import { useState } from 'react';
import { HourglassDisabled, Settings } from '@mui/icons-material';
import { Box, Container, Grid } from '@mui/material';
import { DateTime } from 'luxon';
import { useTranslation } from 'react-i18next';
import { EmptyTable } from '../../../HrTools/Shared/EmptyTable/EmptyTable';
import {
Filters,
SettingsDialog,
getFiltersWithCalculatedDates,
} from '../../StaffExpenseReport/SettingsDialog/SettingsDialog';
import { StyledFilterButton } from '../../StaffExpenseReport/StaffExpenseReport';
import { CardSkeleton } from '../Card/CardSkeleton';
import { ExpensesPieChart } from '../Charts/ExpensesPieChart';
import { MonthlySummaryChart } from '../Charts/MonthlySummaryChart';
import { SummaryBarChart } from '../Charts/SummaryBarChart';
import { ReportTypeEnum } from '../Helper/MPGAReportEnum';
import { FundTypes, ReportTypeEnum } from '../Helper/MPGAReportEnum';
import { TableCard } from '../Tables/TableCard';
import { AllData } from '../mockData';

Expand All @@ -22,73 +30,107 @@
currency,
}) => {
const { t } = useTranslation();
const [isSettingsOpen, setIsSettingsOpen] = useState(false);

const startDate = DateTime.now().minus({ months: 11 }).startOf('month');
const endDate = DateTime.now().endOf('month');

const handleSettingsClick = () => {
setIsSettingsOpen(true);
};

const selectedFilters: Filters = getFiltersWithCalculatedDates({
selectedDateRange: null,
startDate: startDate,
endDate: endDate,
categories: null,
});

return (
<Box mt={2}>
<Container>
<Box mt={2} mb={2}>
<Grid container spacing={2}>
<Grid item xs={7}>
<CardSkeleton title={t('Summary')} subtitle={t('Last 12 Months')}>
<SummaryBarChart aspect={2} width={100} currency={currency} />
</CardSkeleton>
</Grid>
<Grid item xs={5}>
<CardSkeleton
title={t('Expenses Categories')}
subtitle={t('Last 12 Months')}
>
<ExpensesPieChart aspect={1.35} width={100} />
</CardSkeleton>
</Grid>
</Grid>
</Box>
<Box>
<Box display="flex" justifyContent="flex-end" mb={2}>
<StyledFilterButton
variant="outlined"
startIcon={<Settings />}
size="small"
onClick={handleSettingsClick}
>
{t('Report Settings')}
</StyledFilterButton>
</Box>
<Box mt={2}>
<TableCard
type={ReportTypeEnum.Income}
data={data.income ?? []}
emptyPlaceholder={
<EmptyTable
title={t('No Income data available')}
subtitle={t('Data not found in the last 12 months')}
icon={HourglassDisabled}
/>
}
title={t('Income')}
months={last12Months}
/>
</Box>
<Box mt={2}>
<TableCard
type={ReportTypeEnum.Expenses}
data={data.expenses}
emptyPlaceholder={
<EmptyTable
title={t('No Expenses data available')}
subtitle={t('Data not found in the last 12 months')}
icon={HourglassDisabled}
/>
}
title={t('Expenses')}
months={last12Months}
/>
</Box>
<Box mt={2} mb={2}>
<CardSkeleton
title={t('Monthly Summary')}
subtitle={t('Last 12 Months')}
>
<MonthlySummaryChart
incomeData={data.income ?? []}
expenseData={data.expenses ?? []}
months={last12Months}
aspect={2.5}
width={100}
currency={currency}
/>
</CardSkeleton>
</Box>
</Container>
{isSettingsOpen && (
<SettingsDialog
selectedFilters={selectedFilters}
selectedFundType={FundTypes.Primary}
isOpen={isSettingsOpen}
onClose={() => setIsSettingsOpen(false)}
isMpgaReport
/>
)}

Check warning on line 133 in src/components/Reports/MPGAIncomeExpensesReport/DisplayModes/ScreenOnlyReport.tsx

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (main)

❌ New issue: Large Method

ScreenOnlyReport:React.FC<ScreenOnlyReportProps> has 106 lines, threshold = 100. Large functions with many lines of code are generally harder to understand and lower the code health. Avoid adding more lines to this function.
</Box>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
selectedFundType: string | null;
onClose: (filters?: Filters) => void;
time?: DateTime;
isMpgaReport?: boolean;
}

export interface Filters {
Expand Down Expand Up @@ -111,7 +112,7 @@
}
};

const getFiltersWithCalculatedDates = (values: Filters): Filters => {
export const getFiltersWithCalculatedDates = (values: Filters): Filters => {
const finalValues = { ...values };
if (values.selectedDateRange !== null) {
const { startDate, endDate } = calculateDateRange(values.selectedDateRange);
Expand All @@ -127,149 +128,158 @@
selectedFilters,
selectedFundType,
time,
isMpgaReport,
}) => {
const { t } = useTranslation();
const [previewFilters, setPreviewFilters] = useState<Filters | null>(null);

const currentTime = useMemo(
() => time ?? DateTime.now().startOf('month'),
[time],
);

const handleClose = () => {
setPreviewFilters(null);
onClose(selectedFilters);
};

const getQueryVariables = (filterParams: Filters | null) => ({
fundTypes: selectedFundType ? [selectedFundType] : null,
...getStaffExpenseMonthRange(filterParams, currentTime),
});

const {
data: categoryData,
loading: categoryLoading,
error: categoryError,
} = useReportsStaffExpensesQuery({
variables: getQueryVariables(previewFilters ?? selectedFilters ?? null),
skip: !isOpen,
fetchPolicy: 'no-cache',
});

const availableCategories = useMemo(() => {
const categoryFunds: Fund[] =
categoryData?.reportsStaffExpenses?.funds ?? [];

const filtersToUse = previewFilters ?? selectedFilters ?? null;

return getAvailableCategories(categoryFunds, filtersToUse, currentTime);
}, [categoryData, previewFilters, selectedFilters, currentTime]);

const validateAndRefetch = (
validateForm: () => Promise<Record<string, unknown>>,
filters: Filters,
) => {
setTimeout(() => {
validateForm().then((errors) => {
if (Object.keys(errors).length === 0) {
setPreviewFilters(getFiltersWithCalculatedDates(filters));
}
});
}, 0);
};

const initialValues: Filters = {
selectedDateRange: selectedFilters?.selectedDateRange ?? null,
startDate:
selectedFilters?.selectedDateRange === null
? selectedFilters?.startDate
: null,
endDate:
selectedFilters?.selectedDateRange === null
? selectedFilters?.endDate
: null,
categories: selectedFilters?.categories ?? null,
};

const handleSubmit = (values: Filters) => {
setPreviewFilters(null); // Clear preview filters when dialog closes
onClose(getFiltersWithCalculatedDates(values));
};

return (
<Dialog open={isOpen} onClose={handleClose} fullWidth maxWidth="md">
<DialogTitle>{t('Report Settings')}</DialogTitle>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
validateOnChange
validateOnBlur
enableReinitialize
onSubmit={handleSubmit}
>
{({
values,
setFieldValue,
isValid,
dirty,
errors,
touched,
validateForm,
setTouched,
}) => {
const handleCategoryToggle = (category: string, checked: boolean) => {
const selectedCategories = values.categories ?? availableCategories;
const newCategories = checked
? [...selectedCategories, category]
: selectedCategories.filter(
(selectedCategory) => selectedCategory !== category,
);
setFieldValue('categories', newCategories);
};

return (
<Form>
<DialogContent>
<TextField
select
label={t('Select Date Range')}
fullWidth
value={values.selectedDateRange ?? ''}
onChange={(e) => {
const value = e.target.value === '' ? null : e.target.value;
setFieldValue('selectedDateRange', value);
if (value !== null) {
setFieldValue('startDate', null);
setFieldValue('endDate', null);
{!isMpgaReport && (
<>
<TextField
select
label={t('Select Date Range')}
fullWidth
value={values.selectedDateRange ?? ''}
onChange={(e) => {
const value =
e.target.value === '' ? null : e.target.value;
setFieldValue('selectedDateRange', value);
if (value !== null) {
setFieldValue('startDate', null);
setFieldValue('endDate', null);

setTouched({
...touched,
startDate: false,
endDate: false,
});
}
validateAndRefetch(validateForm, {
...values,
selectedDateRange: value as DateRange | null,
...(value !== null && { startDate: null, endDate: null }),
});
}}
>
<MenuItem value="">{t('None')}</MenuItem>
<MenuItem value={DateRange.WeekToDate}>
{t('Week to Date')}
</MenuItem>
<MenuItem value={DateRange.MonthToDate}>
{t('Month to Date')}
</MenuItem>
<MenuItem value={DateRange.YearToDate}>
{t('Year to Date')}
</MenuItem>
</TextField>
setTouched({
...touched,
startDate: false,
endDate: false,
});
}
validateAndRefetch(validateForm, {
...values,
selectedDateRange: value as DateRange | null,
...(value !== null && {
startDate: null,
endDate: null,
}),
});
}}
>
<MenuItem value="">{t('None')}</MenuItem>
<MenuItem value={DateRange.WeekToDate}>
{t('Week to Date')}
</MenuItem>
<MenuItem value={DateRange.MonthToDate}>
{t('Month to Date')}
</MenuItem>
<MenuItem value={DateRange.YearToDate}>
{t('Year to Date')}
</MenuItem>
</TextField>

<Typography variant="body2" sx={{ mt: 2, mb: 2 }}>
{t('Or enter a custom date range:')}
</Typography>
<Typography variant="body2" sx={{ mt: 2, mb: 2 }}>
{t('Or enter a custom date range:')}
</Typography>
</>
)}

Check warning on line 282 in src/components/Reports/StaffExpenseReport/SettingsDialog/SettingsDialog.tsx

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (main)

❌ Getting worse: Complex Method

SettingsDialog:React.FC<SettingsDialogProps> increases in cyclomatic complexity from 32 to 33, threshold = 15. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.

<Box display="flex" gap={2}>
<CustomDateField
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const StyledTimeNavBox = styled(Box)({
gap: theme.spacing(2),
});

const StyledFilterButton = styled(Button)({
export const StyledFilterButton = styled(Button)({
color: theme.palette.mpdxGrayDark.main,
borderColor: theme.palette.mpdxGrayDark.main,
'&:hover': {
Expand Down Expand Up @@ -404,7 +404,7 @@ export const StaffExpenseReport: React.FC<StaffExpenseReportProps> = ({
size="small"
onClick={handleSettingsClick}
>
{t('Filter Settings')}
{t('Report Settings')}
</StyledFilterButton>
</Box>
</Container>
Expand Down
Loading