Skip to content

Commit

Permalink
FFT-159 Refactor payroll endpoints into one (#594)
Browse files Browse the repository at this point in the history
  • Loading branch information
CaitBarnard authored Jan 13, 2025
1 parent 63912f3 commit 7d0f230
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 245 deletions.
183 changes: 77 additions & 106 deletions front_end/src/Apps/Payroll.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,43 +11,40 @@ import PayrollTable from "../Components/EditPayroll/PayrollTable";
import Tabs, { Tab } from "../Components/EditPayroll/Tabs";
import EditPayModifier from "../Components/EditPayroll/EditPayModifier";
import ToggleCheckbox from "../Components/Common/ToggleCheckbox";
import ErrorSummary from "../Components/Common/ErrorSummary";
import SuccessBanner from "../Components/Common/SuccessBanner";

const initialPayrollState = [];
const initialVacanciesState = [];
const initialPayModifiersState = [];
const initialPayrollState = {
employees: [],
vacancies: [],
pay_modifiers: [],
};
const initialPreviousMonthsState = [];

export default function Payroll() {
const [allPayroll, dispatch] = useReducer(
payrollReducer,
initialPayrollState,
);
const [vacancies, dispatchVacancies] = useReducer(
vacanciesReducer,
initialVacanciesState,
);
const [payModifiers, dispatchPayModifiers] = useReducer(
payModifiersReducer,
initialPayModifiersState,
);
const [previousMonths, dispatchPreviousMonths] = useReducer(
previousMonthsReducer,
initialPreviousMonthsState,
);
const [saveSuccess, setSaveSuccess] = useState(false);
const [saveError, setSaveError] = useState(false);
const [activeTab, setActiveTab] = useState(() => {
const savedTab = localStorage.getItem("editPayroll.activeTab");
return savedTab ? parseInt(savedTab) : 0;
});
const initialPreviousMonths = localStorage.getItem(
"editPayroll.hidePreviousMonths",
);
const [hidePreviousMonths, setHidePreviousMonths] = useState(
initialPreviousMonths === "true",
);
const [offset, setOffset] = useState(0);
const [saveSuccess, setSaveSuccess] = useState(false);
const [saveError, setSaveError] = useState(false);
const [activeTab, setActiveTab] = useState(() => {
const savedTab = localStorage.getItem("editPayroll.activeTab");
return savedTab ? parseInt(savedTab) : 0;
});

// Use Effects
useEffect(() => {
localStorage.setItem("editPayroll.activeTab", activeTab);
setSaveSuccess(false);
Expand All @@ -73,31 +70,25 @@ export default function Payroll() {
localStorage.removeItem("editPayroll.saveSuccess");
}

api.getPayrollData().then((data) => dispatch({ type: "fetched", data }));
api
.getVacancyData()
.then((data) => dispatchVacancies({ type: "fetched", data }));
api
.getPayModifierData()
.then((data) => dispatchPayModifiers({ type: "fetched", data }));
api.getPayrollData().then((data) => {
dispatch({ type: "fetched", data });
});
}, []);

// Computed properties
const payroll = useMemo(
() => allPayroll.filter((payroll) => payroll.basic_pay > 0),
() => allPayroll.employees.filter((payroll) => payroll.basic_pay > 0),
[allPayroll],
);
const nonPayroll = useMemo(
() => allPayroll.filter((payroll) => payroll.basic_pay <= 0),
() => allPayroll.employees.filter((payroll) => payroll.basic_pay <= 0),
[allPayroll],
);

// Handlers
async function handleSavePayroll() {
try {
await api.postPayrollData(allPayroll);
await api.postVacancyData(vacancies);
await api.postPayModifierData(payModifiers);

setSaveSuccess(true);
localStorage.setItem("editPayroll.saveSuccess", "true");
Expand All @@ -112,16 +103,17 @@ export default function Payroll() {
}

function handleTogglePayPeriods(id, index, enabled) {
dispatch({ type: "updatePayPeriods", id, index, enabled });
dispatch({ type: "updatePayPeriodsEmployees", id, index, enabled });
}

function handleToggleVacancyPayPeriods(id, index, enabled) {
dispatchVacancies({ type: "updatePayPeriods", id, index, enabled });
dispatch({ type: "updatePayPeriodsVacancies", id, index, enabled });
}

function handleUpdatePayModifiers(id, index, value) {
dispatchPayModifiers({ type: "updatePayModifiers", id, index, value });
dispatch({ type: "updatePayModifiers", id, index, value });
}

function handleHidePreviousMonths() {
setHidePreviousMonths(!hidePreviousMonths);

Expand All @@ -131,40 +123,12 @@ export default function Payroll() {
);
}

const errors = [{ label: "", message: "Error saving payroll data" }];

return (
<>
{saveSuccess && (
<div className="govuk-notification-banner govuk-notification-banner--success">
<div className="govuk-notification-banner__header">
<h2
className="govuk-notification-banner__title"
id="govuk-notification-banner-title"
>
Success
</h2>
</div>
</div>
)}
{saveError && (
<div
className="govuk-error-summary"
aria-labelledby="error-summary-title"
role="alert"
tabIndex="-1"
data-module="govuk-error-summary"
>
<h2 className="govuk-error-summary__title" id="error-summary-title">
There is a problem
</h2>
<div className="govuk-error-summary__body">
<ul className="govuk-list govuk-error-summary__list">
<li>
<a href="#">Error saving payroll data</a>
</li>
</ul>
</div>
</div>
)}
{saveSuccess && <SuccessBanner />}
{saveError && <ErrorSummary errors={errors} />}
<ToggleCheckbox
toggle={hidePreviousMonths}
handler={handleHidePreviousMonths}
Expand Down Expand Up @@ -195,7 +159,7 @@ export default function Payroll() {
</Tab>
<Tab label="Vacancies" key="3">
<PayrollTable
payroll={vacancies}
payroll={allPayroll.vacancies}
headers={vacancyHeaders}
onTogglePayPeriods={handleToggleVacancyPayPeriods}
RowComponent={VacancyRow}
Expand All @@ -211,7 +175,7 @@ export default function Payroll() {
</Tab>
<Tab label="Pay modifiers" key="4">
<EditPayModifier
data={payModifiers}
data={allPayroll.pay_modifiers}
onInputChange={handleUpdatePayModifiers}
/>
</Tab>
Expand All @@ -223,54 +187,64 @@ export default function Payroll() {
);
}

const positionReducer = (data, action) => {
switch (action.type) {
case "fetched": {
return action.data;
function updatePayPeriods(data, action) {
return data.map((row) => {
if (row.id === action.id) {
const updatedPayPeriods = row.pay_periods.map((period, index) => {
if (index + 1 >= action.index + 1) {
return !action.enabled;
}
return period;
});
return {
...row,
pay_periods: updatedPayPeriods,
};
}
case "updatePayPeriods": {
return data.map((row) => {
if (row.id === action.id) {
const updatedPayPeriods = row.pay_periods.map((period, index) => {
if (index + 1 >= action.index + 1) {
return !action.enabled;
}
return period;
});
return {
...row,
pay_periods: updatedPayPeriods,
};
return row;
});
}

function updatePayModifiers(data, action) {
return data.map((row) => {
if (row.id === action.id) {
const updatedPayModifier = row.pay_modifiers.map((modifier, index) => {
if (index === action.index) {
return parseFloat(action.value);
}
return row;
return modifier;
});
return {
...row,
pay_modifiers: updatedPayModifier,
};
}
}
};
return row;
});
}

const payModifiersReducer = (data, action) => {
const payrollReducer = (data, action) => {
switch (action.type) {
case "fetched": {
return action.data;
}
case "updatePayPeriodsEmployees": {
return {
...data,
employees: updatePayPeriods(data.employees, action),
};
}
case "updatePayPeriodsVacancies": {
return {
...data,
vacancies: updatePayPeriods(data.vacancies, action),
};
}
case "updatePayModifiers": {
return data.map((row) => {
if (row.id === action.id) {
const updatedPayModifier = row.pay_modifiers.map(
(modifier, index) => {
if (index === action.index) {
return parseFloat(action.value);
}
return modifier;
},
);
return {
...row,
pay_modifiers: updatedPayModifier,
};
}
return row;
});
return {
...data,
pay_modifiers: updatePayModifiers(data.pay_modifiers, action),
};
}
}
};
Expand All @@ -282,6 +256,3 @@ const previousMonthsReducer = (data, action) => {
}
}
};

const payrollReducer = (data, action) => positionReducer(data, action);
const vacanciesReducer = (data, action) => positionReducer(data, action);
26 changes: 26 additions & 0 deletions front_end/src/Components/Common/ErrorSummary/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export default function ErrorSummary({ errors }) {
return (
<div
className="govuk-error-summary"
aria-labelledby="error-summary-title"
role="alert"
tabIndex="-1"
data-module="govuk-error-summary"
>
<h2 className="govuk-error-summary__title" id="error-summary-title">
There is a problem
</h2>
<div className="govuk-error-summary__body">
<ul className="govuk-list govuk-error-summary__list">
{errors.map((error) => {
return (
<li key={error.label}>
<a href={`#${error.label}`}>{error.message}</a>
</li>
);
})}
</ul>
</div>
</div>
);
}
14 changes: 14 additions & 0 deletions front_end/src/Components/Common/SuccessBanner/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default function SuccessBanner() {
return (
<div className="govuk-notification-banner govuk-notification-banner--success">
<div className="govuk-notification-banner__header">
<h2
className="govuk-notification-banner__title"
id="govuk-notification-banner-title"
>
Success
</h2>
</div>
</div>
);
}
Loading

0 comments on commit 7d0f230

Please sign in to comment.