Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit c5c670f

Browse files
committed
Implemented period data updating from server.
1 parent 4b8dbf2 commit c5c670f

File tree

9 files changed

+167
-50
lines changed

9 files changed

+167
-50
lines changed

src/routes/WorkPeriods/components/PaymentStatus/styles.module.scss

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@import "styles/mixins";
2+
@import "styles/variables";
23

34
.container {
45
display: inline-block;
@@ -25,7 +26,10 @@
2526
background: #9d41c9;
2627
}
2728

28-
.cancelled,
29+
.cancelled {
30+
background: #999;
31+
}
32+
2933
.failed {
3034
background: #da0000;
3135
}
@@ -37,4 +41,5 @@
3741
line-height: 20px;
3842
letter-spacing: normal;
3943
background: transparent;
44+
color: $text-color;
4045
}

src/routes/WorkPeriods/components/PeriodItem/index.jsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,12 @@ const PeriodItem = ({
123123
</td>
124124
<td className={styles.paymentTotal}>
125125
<span className={styles.paymentTotalSum}>
126-
{currencyFormatter.format(item.paymentTotal)}
126+
{currencyFormatter.format(data.paymentTotal)}
127127
</span>
128128
<span className={styles.daysPaid}> ({data.daysPaid})</span>
129129
</td>
130130
<td>
131-
<PaymentStatus status={item.paymentStatus} />
131+
<PaymentStatus status={data.paymentStatus} />
132132
</td>
133133
<td className={styles.daysWorked}>
134134
<IntegerField
@@ -167,12 +167,12 @@ PeriodItem.propTypes = {
167167
startDate: PT.string.isRequired,
168168
endDate: PT.string.isRequired,
169169
weeklyRate: PT.number,
170-
paymentStatus: PT.string.isRequired,
171-
paymentTotal: PT.number.isRequired,
172170
}).isRequired,
173171
data: PT.shape({
174172
daysWorked: PT.number.isRequired,
175173
daysPaid: PT.number.isRequired,
174+
paymentStatus: PT.string.isRequired,
175+
paymentTotal: PT.number.isRequired,
176176
}).isRequired,
177177
details: PT.shape({
178178
periodId: PT.string.isRequired,

src/routes/WorkPeriods/components/PeriodsHistoryItem/index.jsx

+5-5
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,15 @@ const PeriodsHistoryItem = ({ isDisabled, item, data, currentStartDate }) => {
6666
<PeriodsHistoryPaymentTotal
6767
className={styles.paymentTotalContainer}
6868
payments={item.payments}
69-
paymentTotal={item.paymentTotal}
69+
paymentTotal={data.paymentTotal}
7070
daysPaid={data.daysPaid}
7171
/>
7272
</td>
7373
<td className={styles.paymentStatus}>
74-
<PaymentStatus status={item.paymentStatus} />
74+
<PaymentStatus status={data.paymentStatus} />
7575
</td>
7676
<td className={styles.daysWorked}>
77-
{item.paymentStatus === PAYMENT_STATUS.PAID ? (
77+
{data.paymentStatus === PAYMENT_STATUS.PAID ? (
7878
`${daysWorked} ${daysWorked === 1 ? "Day" : "Days"}`
7979
) : (
8080
<IntegerField
@@ -98,14 +98,14 @@ PeriodsHistoryItem.propTypes = {
9898
id: PT.string.isRequired,
9999
startDate: PT.oneOfType([PT.string, PT.number]).isRequired,
100100
endDate: PT.oneOfType([PT.string, PT.number]).isRequired,
101-
paymentStatus: PT.string.isRequired,
102101
payments: PT.array,
103-
paymentTotal: PT.number.isRequired,
104102
weeklyRate: PT.number,
105103
}).isRequired,
106104
data: PT.shape({
107105
daysWorked: PT.number.isRequired,
108106
daysPaid: PT.number.isRequired,
107+
paymentStatus: PT.string.isRequired,
108+
paymentTotal: PT.number.isRequired,
109109
}).isRequired,
110110
currentStartDate: PT.oneOfType([PT.string, PT.number, PT.object]).isRequired,
111111
};

src/services/workPeriods.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,20 @@ export const fetchResourceBookings = (params) => {
105105
*
106106
* @param {string} periodId working period id
107107
* @param {number} daysWorked new number of working days
108-
* @returns {Promise}
108+
* @returns {[Promise, Object]}
109109
*/
110110
export const patchWorkPeriodWorkingDays = (periodId, daysWorked) => {
111-
return axios.patch(`${WORK_PERIODS_API_URL}/${periodId}`, { daysWorked });
111+
const source = CancelToken.source();
112+
return [
113+
axios
114+
.patch(
115+
`${WORK_PERIODS_API_URL}/${periodId}`,
116+
{ daysWorked },
117+
{ cancelToken: source.token }
118+
)
119+
.then(extractResponseData),
120+
source,
121+
];
112122
};
113123

114124
/**

src/store/actionTypes/workPeriods.js

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ export const WP_SET_DETAILS_HIDE_PAST_PERIODS =
1919
"WP_SET_DETAILS_HIDE_PAST_PERIODS";
2020
export const WP_SET_PAGE_NUMBER = "WP_SET_PAGE_NUMBER";
2121
export const WP_SET_PAGE_SIZE = "WP_SET_PAGE_SIZE";
22+
export const WP_SET_DATA_PENDING = "WP_SET_DATA_PENDING";
23+
export const WP_SET_DATA_SUCCESS = "WP_SET_DATA_SUCCESS";
24+
export const WP_SET_DATA_ERROR = "WP_SET_DATA_ERROR";
2225
export const WP_SET_DATE_RANGE = "WP_SET_DATE_RANGE";
2326
export const WP_SET_SORT_BY = "WP_SET_SORT_BY";
2427
export const WP_SET_SORT_ORDER = "WP_SET_SORT_ORDER";

src/store/actions/workPeriods.js

+22
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,28 @@ export const setWorkPeriodWorkingDays = (periodId, daysWorked) => ({
318318
payload: { periodId, daysWorked },
319319
});
320320

321+
/**
322+
* Creates an action denoting the update of working period's changeable data.
323+
*
324+
* @param {Object} periodId working period id
325+
* @param {Object} cancelSource axios cancel token source
326+
* @returns {Object}
327+
*/
328+
export const setWorkPeriodDataPending = (periodId, cancelSource) => ({
329+
type: ACTION_TYPE.WP_SET_DATA_PENDING,
330+
payload: { periodId, cancelSource },
331+
});
332+
333+
export const setWorkPeriodDataSuccess = (periodId, data) => ({
334+
type: ACTION_TYPE.WP_SET_DATA_SUCCESS,
335+
payload: { periodId, data },
336+
});
337+
338+
export const setWorkPeriodDataError = (periodId, message) => ({
339+
type: ACTION_TYPE.WP_SET_DATA_ERROR,
340+
payload: { periodId, message },
341+
});
342+
321343
/**
322344
* Creates an action to toggle certain working period by its id.
323345
*

src/store/reducers/workPeriods.js

+55-20
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,6 @@ const initFilters = () => ({
3636
userHandle: "",
3737
});
3838

39-
const initPeriodData = (daysWorked, daysPaid) => ({
40-
cancelSource: null,
41-
daysWorked,
42-
daysPaid,
43-
});
44-
4539
const initPeriodDetails = (
4640
periodId,
4741
rbId,
@@ -125,13 +119,9 @@ const actionHandlers = {
125119
: oldPagination;
126120
const periodsData = {};
127121
for (let period of periods) {
128-
periodsData[period.id] = initPeriodData(
129-
period.daysWorked,
130-
period.daysPaid
131-
);
132-
// These two lines can be removed but they're kept for now for debugging.
133-
delete period.daysWorked;
134-
delete period.daysPaid;
122+
period.data.cancelSource = null;
123+
periodsData[period.id] = period.data;
124+
delete period.data;
135125
}
136126
return {
137127
...state,
@@ -215,13 +205,9 @@ const actionHandlers = {
215205
}
216206
const periodsData = state.periodsData[0];
217207
for (let period of details.periods) {
218-
periodsData[period.id] = initPeriodData(
219-
period.daysWorked,
220-
period.daysPaid
221-
);
222-
// These two lines can be removed but they're kept for now for debugging.
223-
delete period.daysWorked;
224-
delete period.daysPaid;
208+
period.data.cancelSource = null;
209+
periodsData[period.id] = period.data;
210+
delete period.data;
225211
}
226212
periodDetails = {
227213
...periodDetails,
@@ -528,9 +514,58 @@ const actionHandlers = {
528514
},
529515
};
530516
},
517+
[ACTION_TYPE.WP_SET_DATA_PENDING]: (state, { periodId, cancelSource }) => {
518+
const periodsData = state.periodsData[0];
519+
const periodData = periodsData[periodId];
520+
if (!periodData) {
521+
return state;
522+
}
523+
periodsData[periodId] = {
524+
...periodData,
525+
cancelSource,
526+
};
527+
return {
528+
...state,
529+
periodsData: [periodsData],
530+
};
531+
},
532+
[ACTION_TYPE.WP_SET_DATA_SUCCESS]: (state, { periodId, data }) => {
533+
const periodsData = state.periodsData[0];
534+
const periodData = periodsData[periodId];
535+
if (!periodData) {
536+
return state;
537+
}
538+
periodsData[periodId] = {
539+
...periodData,
540+
...data,
541+
cancelSource: null,
542+
};
543+
return {
544+
...state,
545+
periodsData: [periodsData],
546+
};
547+
},
548+
[ACTION_TYPE.WP_SET_DATA_ERROR]: (state, { periodId }) => {
549+
const periodsData = state.periodsData[0];
550+
const periodData = periodsData[periodId];
551+
if (!periodData) {
552+
return state;
553+
}
554+
periodsData[periodId] = {
555+
...periodData,
556+
cancelSource: null,
557+
};
558+
return {
559+
...state,
560+
periodsData: [periodsData],
561+
};
562+
},
531563
[ACTION_TYPE.WP_SET_WORKING_DAYS]: (state, { periodId, daysWorked }) => {
532564
const periodsData = state.periodsData[0];
533565
const periodData = periodsData[periodId];
566+
if (!periodData) {
567+
return state;
568+
}
534569
daysWorked = Math.min(Math.max(daysWorked, periodData.daysPaid), 5);
535570
if (daysWorked === periodData.daysWorked) {
536571
return state;

src/store/thunks/workPeriods.js

+38-8
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
import {
2020
normalizeBillingAccounts,
2121
normalizeDetailsPeriodItems,
22+
normalizePeriodData,
2223
normalizePeriodItems,
2324
} from "utils/workPeriods";
2425
import { makeToast } from "components/ToastrMessage";
@@ -217,20 +218,49 @@ export const updateWorkPeriodBillingAccount =
217218
};
218219

219220
/**
221+
* Sends an update request to the server to update the number of working
222+
* period's working days. The working period is also updated with the data
223+
* from response.
220224
*
221-
* @param {string} periodId
222-
* @param {number} daysWorked
225+
* @param {string} periodId working period id
226+
* @param {number} daysWorked working period's working days
223227
* @returns {function}
224228
*/
225229
export const updateWorkPeriodWorkingDays =
226-
(periodId, daysWorked) => async () => {
230+
(periodId, daysWorked) => async (dispatch, getState) => {
231+
let [periodsData] = selectors.getWorkPeriodsData(getState());
232+
periodsData[periodId]?.cancelSource?.cancel();
233+
const [promise, source] = services.patchWorkPeriodWorkingDays(
234+
periodId,
235+
daysWorked
236+
);
237+
dispatch(actions.setWorkPeriodDataPending(periodId, source));
238+
let periodData = null;
239+
let errorMessage = null;
227240
try {
228-
await services.patchWorkPeriodWorkingDays(periodId, daysWorked);
241+
const data = await promise;
242+
periodData = normalizePeriodData(data);
229243
} catch (error) {
230-
makeToast(
231-
`Failed to update working days for working period ${periodId}.\n` +
232-
error.toString()
233-
);
244+
if (!axios.isCancel(error)) {
245+
errorMessage = error.toString();
246+
makeToast(
247+
`Failed to update working days for working period ${periodId}.\n` +
248+
errorMessage
249+
);
250+
}
251+
}
252+
[periodsData] = selectors.getWorkPeriodsData(getState());
253+
const currentDaysWorked = periodsData[periodId]?.daysWorked;
254+
// If periodData is null it means the request was cancelled right before
255+
// another request was sent and so we don't need to update the state.
256+
// If periodData's daysWorked is not equal to the current daysWorked
257+
// it means that the state was changed while the data was in transit
258+
// and there will be a new request at the end of which the period's data
259+
// will be updated so again we don't need to update the state.
260+
if (periodData && periodData.daysWorked === currentDaysWorked) {
261+
dispatch(actions.setWorkPeriodDataSuccess(periodId, periodData));
262+
} else if (errorMessage) {
263+
dispatch(actions.setWorkPeriodDataError(periodId, errorMessage));
234264
}
235265
};
236266

src/utils/workPeriods.js

+22-10
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export function normalizePeriodItems(items) {
1111
for (let item of items) {
1212
const workPeriod = item.workPeriods?.[0] || empty;
1313
const billingAccountId = item.billingAccountId;
14-
const daysWorked = workPeriod.daysWorked;
1514
periods.push({
1615
id: workPeriod.id || item.id,
1716
rbId: item.id,
@@ -25,15 +24,32 @@ export function normalizePeriodItems(items) {
2524
: "",
2625
endDate: item.endDate ? moment(item.endDate).format(DATE_FORMAT_UI) : "",
2726
weeklyRate: item.memberRate,
28-
paymentStatus: normalizePaymentStatus(workPeriod.paymentStatus),
29-
paymentTotal: +workPeriod.paymentTotal || 0,
30-
daysWorked: daysWorked === null ? 5 : +daysWorked || 0,
31-
daysPaid: +workPeriod.daysPaid || 0,
27+
data: normalizePeriodData(workPeriod),
3228
});
3329
}
3430
return periods;
3531
}
3632

33+
/**
34+
* Normalizes specific working period data (daysWorked, daysPaid,
35+
* paymentStatus, paymentTotal).
36+
*
37+
* @param {Object} period
38+
* @param {number} period.daysWorked
39+
* @param {number} period.daysPaid
40+
* @param {string} period.paymentStatus
41+
* @param {number} period.paymentTotal
42+
* @returns {Object}
43+
*/
44+
export function normalizePeriodData(period) {
45+
return {
46+
daysWorked: period.daysWorked === null ? 5 : +period.daysWorked || 0,
47+
daysPaid: +period.daysPaid || 0,
48+
paymentStatus: normalizePaymentStatus(period.paymentStatus),
49+
paymentTotal: +period.paymentTotal || 0,
50+
};
51+
}
52+
3753
/**
3854
* Creates options to be used in dropdown selecting working period's
3955
* billing account.
@@ -69,17 +85,13 @@ export function createAssignedBillingAccountOption(accountId) {
6985
export function normalizeDetailsPeriodItems(items) {
7086
const periods = [];
7187
for (let item of items) {
72-
const daysWorked = item.daysWorked;
7388
periods.push({
7489
id: item.id,
7590
startDate: item.startDate ? moment(item.startDate).valueOf() : 0,
7691
endDate: item.endDate ? moment(item.endDate).valueOf() : 0,
77-
paymentStatus: normalizePaymentStatus(item.paymentStatus),
7892
payments: item.payments || [],
79-
paymentTotal: +item.paymentTotal || 0,
8093
weeklyRate: item.memberRate,
81-
daysWorked: daysWorked === null ? 5 : +daysWorked || 0,
82-
daysPaid: +item.daysPaid || 0,
94+
data: normalizePeriodData(item),
8395
});
8496
}
8597
periods.sort(sortByStartDate);

0 commit comments

Comments
 (0)