@bjsdistribution/dms-client-return 0.1.21-prod → 0.1.22-dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/common/custom-input/CustomInput.d.ts +5 -0
- package/dist/components/order-view/order-actions/actions/send-sms/SendSmsModal.d.ts +1 -0
- package/dist/components/order-view/order-actions/actions/what3words/What3WordsModal.tsx.d.ts +3 -0
- package/dist/components/order-view/store.d.ts +1 -1
- package/dist/components/order-view/types.d.ts +5 -4
- package/dist/components/order-view/utils.d.ts +2 -1
- package/dist/components/store.d.ts +2 -1
- package/dist/dms-client-return.cjs.development.js +1278 -903
- package/dist/dms-client-return.cjs.development.js.map +1 -1
- package/dist/dms-client-return.cjs.production.min.js +1 -1
- package/dist/dms-client-return.cjs.production.min.js.map +1 -1
- package/dist/dms-client-return.esm.js +1280 -905
- package/dist/dms-client-return.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/common/custom-input/CustomInput.tsx +24 -0
- package/src/components/client-portal/DateFilterStore.ts +57 -9
- package/src/components/client-portal/OutstandingTable.tsx +1 -1
- package/src/components/client-portal/ReturnStatus.tsx +84 -44
- package/src/components/client-portal/return-confirm/ConfirmedMain.tsx +5 -6
- package/src/components/client-portal/return-confirm/ReturnConfirmedTable.tsx +21 -2
- package/src/components/order-daily-overview/header/route-header/ClientRouterHeader.tsx +45 -23
- package/src/components/order-daily-overview/header-filters/HeaderFilter.tsx +3 -2
- package/src/components/order-daily-overview/header-filters/SelectionFilter.tsx +5 -3
- package/src/components/order-daily-overview/index.tsx +12 -5
- package/src/components/order-daily-overview/return-order-table/return-orders-filters/ReturnFilter.tsx +53 -4
- package/src/components/order-view/details-filters/left-side-filters/dropdowns/DropDowns.tsx +48 -12
- package/src/components/order-view/general-information/dropdowns/Print.tsx +13 -17
- package/src/components/order-view/modalsPopup/ModalManager.tsx +9 -1
- package/src/components/order-view/notes/logs/index.tsx +3 -1
- package/src/components/order-view/order-actions/actions/add-resources/DocFileUpload.tsx +4 -4
- package/src/components/order-view/order-actions/actions/charge-log/index.tsx +26 -1
- package/src/components/order-view/order-actions/actions/confirm-order/customer-info/DeliveryDate.tsx +1 -3
- package/src/components/order-view/order-actions/actions/create-task-reminder/CreateReminderModal.tsx +7 -4
- package/src/components/order-view/order-actions/actions/force-debrief/ForceDebriefModal.tsx +21 -20
- package/src/components/order-view/order-actions/actions/send-sms/SendSmsModal.tsx +104 -51
- package/src/components/order-view/order-actions/actions/what3words/What3WordsModal.tsx.tsx +306 -0
- package/src/components/order-view/order-actions/index.tsx +34 -13
- package/src/components/order-view/order-details/expanded-content/customer-section/contact-info/ContactHome.tsx +6 -3
- package/src/components/order-view/order-details/expanded-content/customer-section/contact-info/ContactMobile.tsx +6 -3
- package/src/components/order-view/order-details/expanded-content/customer-section/contact-info/ContactWork.tsx +9 -3
- package/src/components/order-view/order-details/expanded-content/customer-section/location-info/Latitude.tsx +5 -7
- package/src/components/order-view/order-details/expanded-content/customer-section/location-info/Longitude.tsx +1 -1
- package/src/components/order-view/order-details/expanded-content/customer-section/location-info/What3words.tsx +45 -13
- package/src/components/order-view/order-details/expanded-content/footer/index.tsx +63 -27
- package/src/components/order-view/order-details/expanded-content/orders-section/DeliveryDate.tsx +1 -1
- package/src/components/order-view/products-info/index.tsx +1 -0
- package/src/components/order-view/types.ts +6 -4
- package/src/components/store.ts +8 -1
- package/src/index.tsx +6 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
StyledFIlterELements,
|
|
4
4
|
StyledFiter,
|
|
@@ -6,14 +6,32 @@ import {
|
|
|
6
6
|
Text,
|
|
7
7
|
} from '../../filters/FilterStyle';
|
|
8
8
|
import RightFilters from './RightFilters';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
useAtom,
|
|
11
|
+
useClientPortal,
|
|
12
|
+
useCustomerData,
|
|
13
|
+
useSearchAfterText,
|
|
14
|
+
useSearchText,
|
|
15
|
+
useStatsCPCount,
|
|
16
|
+
} from '../../../store';
|
|
10
17
|
import { useParams } from 'react-router-dom';
|
|
11
18
|
import ReturnRTFilters from '../../../client-portal/ReturnRTFilters';
|
|
19
|
+
import { returnCountType } from '../../../client-portal/ReturnStatus';
|
|
20
|
+
import { useAxios } from '@bjsdistribution/dms-component-library';
|
|
21
|
+
import { dateFilterCPReducer } from '../../../client-portal/DateFilterStore';
|
|
12
22
|
|
|
13
23
|
const ReturnFilter = () => {
|
|
14
24
|
const [customers] = useCustomerData();
|
|
15
25
|
const [clientPortal] = useClientPortal();
|
|
16
26
|
const { status } = useParams();
|
|
27
|
+
const [count, setCount] = useStatsCPCount();
|
|
28
|
+
const [debouncedSearchAfterText] = useSearchAfterText();
|
|
29
|
+
const [debouncedSearchText] = useSearchText();
|
|
30
|
+
|
|
31
|
+
const [state] = useAtom(dateFilterCPReducer);
|
|
32
|
+
const { 'x-filters': filters } = state;
|
|
33
|
+
|
|
34
|
+
const cleanedFilters = filters.filter(f => f.field !== 'order_status');
|
|
17
35
|
|
|
18
36
|
const queryParams = new URLSearchParams(location.search);
|
|
19
37
|
const customerId = queryParams.get('customerId');
|
|
@@ -32,7 +50,8 @@ const ReturnFilter = () => {
|
|
|
32
50
|
case 'scheduled_for_return':
|
|
33
51
|
return 'All Scheduled Orders';
|
|
34
52
|
case 'return_completed':
|
|
35
|
-
return 'View Return Order';
|
|
53
|
+
// return 'View Return Order';
|
|
54
|
+
return 'Completed Return';
|
|
36
55
|
default:
|
|
37
56
|
return status
|
|
38
57
|
.replace(/_/g, ' ')
|
|
@@ -40,6 +59,31 @@ const ReturnFilter = () => {
|
|
|
40
59
|
}
|
|
41
60
|
};
|
|
42
61
|
|
|
62
|
+
const { data: statsData, loading: statsApiLoading, fetchData } = useAxios<{
|
|
63
|
+
data: returnCountType[];
|
|
64
|
+
}>({
|
|
65
|
+
endpoint: `v1/portal/return/stats/`,
|
|
66
|
+
method: 'GET',
|
|
67
|
+
pagination: { is_pagination: false },
|
|
68
|
+
filters: cleanedFilters,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (
|
|
73
|
+
clientPortal &&
|
|
74
|
+
debouncedSearchAfterText === '' &&
|
|
75
|
+
status == 'return_completed'
|
|
76
|
+
) {
|
|
77
|
+
fetchData();
|
|
78
|
+
}
|
|
79
|
+
}, [debouncedSearchAfterText, debouncedSearchText, JSON.stringify(filters)]);
|
|
80
|
+
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (statsData?.data && typeof statsData?.data === 'object') {
|
|
83
|
+
setCount(statsData?.data);
|
|
84
|
+
}
|
|
85
|
+
}, [statsApiLoading]);
|
|
86
|
+
|
|
43
87
|
return (
|
|
44
88
|
<>
|
|
45
89
|
<StyledFiter>
|
|
@@ -53,7 +97,12 @@ const ReturnFilter = () => {
|
|
|
53
97
|
</>
|
|
54
98
|
) : (
|
|
55
99
|
<>
|
|
56
|
-
<Text>
|
|
100
|
+
<Text>
|
|
101
|
+
{status
|
|
102
|
+
? `${getStatusLabel(status)} (${count?.completed_count})`
|
|
103
|
+
: ''}
|
|
104
|
+
</Text>
|
|
105
|
+
|
|
57
106
|
<ReturnRTFilters />
|
|
58
107
|
</>
|
|
59
108
|
)}
|
|
@@ -1,32 +1,68 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
import OptionsDropDown from './OptionsDropDown';
|
|
4
4
|
import Print from '../../../general-information/dropdowns/Print';
|
|
5
5
|
import BjsMenuDropDown from './MenuDropDown';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
errorAtom,
|
|
8
|
+
useAtom,
|
|
9
|
+
useCurrentAction,
|
|
10
|
+
useOrderDetails,
|
|
11
|
+
} from '../../../store';
|
|
7
12
|
// components
|
|
8
13
|
|
|
9
14
|
function DropDowns() {
|
|
10
15
|
const [orderDetails] = useOrderDetails();
|
|
11
16
|
const [, setCurrentAction] = useCurrentAction();
|
|
17
|
+
const [errors] = useAtom(errorAtom);
|
|
18
|
+
const [hasNotifiedMobile, setHasNotifiedMobile] = useState(false);
|
|
19
|
+
const [hasNotifiedEmail, setHasNotifiedEmail] = useState(false);
|
|
12
20
|
|
|
13
21
|
const isEmailId = orderDetails?.order_recipient?.contact_email;
|
|
14
|
-
const isContactNum = orderDetails?.order_recipient?.contact_home;
|
|
22
|
+
// const isContactNum = orderDetails?.order_recipient?.contact_home;
|
|
23
|
+
const VALID_UK_MOBILE_REGEX = /^(07\d{8,12}|447\d{7,11})$/;
|
|
24
|
+
const VALID_IRISH_MOBILE_REGEX = /^(08[35679]\d{7}|3538[35679]\d{7}|\+3538[35679]\d{7})$/;
|
|
25
|
+
|
|
26
|
+
const isValidUKMobile = (number: string | null | undefined): boolean =>
|
|
27
|
+
typeof number === 'string' && VALID_UK_MOBILE_REGEX.test(number);
|
|
28
|
+
const isValidIrishMobile = (number: string | null | undefined): boolean =>
|
|
29
|
+
typeof number === 'string' && VALID_IRISH_MOBILE_REGEX.test(number);
|
|
30
|
+
const isValidMobile = (number: string | null | undefined): boolean =>
|
|
31
|
+
isValidUKMobile(number) || isValidIrishMobile(number);
|
|
32
|
+
|
|
33
|
+
const contactMobile = orderDetails?.order_recipient?.contact_mobile;
|
|
34
|
+
const contactHome = orderDetails?.order_recipient?.contact_home;
|
|
35
|
+
const contactWork = orderDetails?.order_recipient?.contact_work;
|
|
36
|
+
|
|
37
|
+
const allContacts = [contactMobile, contactHome, contactWork];
|
|
38
|
+
const hasValidMobile = allContacts.some(isValidMobile);
|
|
15
39
|
|
|
16
40
|
const actions = [];
|
|
17
41
|
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
42
|
+
if (hasValidMobile) {
|
|
43
|
+
if (errors?.mobileError) {
|
|
44
|
+
if (!hasNotifiedMobile) {
|
|
45
|
+
setHasNotifiedMobile(true);
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
actions.push({
|
|
49
|
+
label: 'Send SMS',
|
|
50
|
+
onClick: () => setCurrentAction('send-sms'),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
23
53
|
}
|
|
24
54
|
|
|
25
55
|
if (isEmailId) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
56
|
+
if (errors?.emailError) {
|
|
57
|
+
if (!hasNotifiedEmail) {
|
|
58
|
+
setHasNotifiedEmail(true);
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
actions.push({
|
|
62
|
+
label: 'Send Email',
|
|
63
|
+
onClick: () => setCurrentAction('send-email'),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
30
66
|
}
|
|
31
67
|
|
|
32
68
|
return (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
import { PrintMenu } from '../../fakedata';
|
|
4
|
-
import { useOrderDetails
|
|
4
|
+
import { useOrderDetails } from '../../store';
|
|
5
5
|
import { Dropdown, Space } from 'antd';
|
|
6
6
|
import ArrowDownIcon from '../../../../assets/icons/ArrowDownIcon';
|
|
7
7
|
import { getBaseEndpoint } from '@bjsdistribution/dms-component-library';
|
|
@@ -13,11 +13,6 @@ function Print() {
|
|
|
13
13
|
const orderId = useMemo(() => {
|
|
14
14
|
return orderDetails?.id;
|
|
15
15
|
}, [orderDetails]);
|
|
16
|
-
//new
|
|
17
|
-
const [orderStaticData] = useOrderStaticData();
|
|
18
|
-
const orderTypeID = useMemo(() => {
|
|
19
|
-
return orderDetails?.order_type_id;
|
|
20
|
-
}, [orderDetails]);
|
|
21
16
|
|
|
22
17
|
const getEnvironment = ():
|
|
23
18
|
| 'development'
|
|
@@ -100,17 +95,18 @@ function Print() {
|
|
|
100
95
|
}
|
|
101
96
|
};
|
|
102
97
|
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
return false;
|
|
98
|
+
const optionToLabelKey: Record<string, keyof typeof orderDetails.labels> = {
|
|
99
|
+
proofofdelivery: 'deliver',
|
|
100
|
+
returnlabels: 'return',
|
|
101
|
+
returncovernote: 'collect',
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const isOptionDisabled = (optionValue: string) => {
|
|
105
|
+
const labels = orderDetails?.labels || {};
|
|
106
|
+
const labelKey = optionToLabelKey[optionValue];
|
|
107
|
+
|
|
108
|
+
if (!labelKey) return false;
|
|
109
|
+
return !labels[labelKey];
|
|
114
110
|
};
|
|
115
111
|
|
|
116
112
|
const filteredOptions = PrintMenu?.map((option: any) => ({
|
|
@@ -103,12 +103,20 @@ const ModalManager = () => {
|
|
|
103
103
|
setShouldPerformAction(false);
|
|
104
104
|
setSelectAll(false);
|
|
105
105
|
setError(null);
|
|
106
|
+
//@ts-ignore
|
|
107
|
+
const orderId = response?.data?.[0]?.order_id;
|
|
108
|
+
|
|
109
|
+
if (orderId) {
|
|
110
|
+
navigate(`/orders/view/return/?order=${orderId}`);
|
|
111
|
+
} else {
|
|
112
|
+
navigate('/orders/view/return/overview');
|
|
113
|
+
}
|
|
106
114
|
|
|
107
115
|
// for local
|
|
108
116
|
// navigate('/overview');
|
|
109
117
|
|
|
110
118
|
// for dev
|
|
111
|
-
navigate('/orders/view/return/overview');
|
|
119
|
+
//navigate('/orders/view/return/overview');
|
|
112
120
|
}
|
|
113
121
|
}, [postLoading]);
|
|
114
122
|
|
|
@@ -11,7 +11,9 @@ function AuditLogs() {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
const Container = styled.div`
|
|
14
|
-
height:
|
|
14
|
+
height: 500px;
|
|
15
|
+
min-height: 200px;
|
|
16
|
+
max-height: 500px;
|
|
15
17
|
overflow: auto; /* Enable scrolling */
|
|
16
18
|
scrollbar-width: thin;
|
|
17
19
|
`;
|
|
@@ -7,8 +7,8 @@ import { notification } from 'antd';
|
|
|
7
7
|
// Store uploaded files
|
|
8
8
|
export const uploadedFilesAtom = atom<File[]>([]);
|
|
9
9
|
|
|
10
|
-
// Allowed
|
|
11
|
-
const allowedFileTypes = ['image/jpeg', 'image/png'];
|
|
10
|
+
// Allowed file MIME types
|
|
11
|
+
const allowedFileTypes = ['image/jpeg', 'image/png', 'application/pdf'];
|
|
12
12
|
|
|
13
13
|
function DocFileUpload() {
|
|
14
14
|
const [uploadedFiles, setUploadedFiles] = useAtom(uploadedFilesAtom);
|
|
@@ -53,7 +53,7 @@ function DocFileUpload() {
|
|
|
53
53
|
if (rejectedFiles?.length > 0) {
|
|
54
54
|
notification.error({
|
|
55
55
|
message: 'Some files were not added',
|
|
56
|
-
description: 'Either they were duplicates or not JPEG/PNG format.',
|
|
56
|
+
description: 'Either they were duplicates or not JPEG/PNG/PDF format.',
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
};
|
|
@@ -112,7 +112,7 @@ function DocFileUpload() {
|
|
|
112
112
|
</IconContainer>
|
|
113
113
|
<TextContainer>
|
|
114
114
|
<Text1>or drag and drop your files</Text1>
|
|
115
|
-
<Text2>Photos (JPEG, PNG)
|
|
115
|
+
<Text2>Photos (JPEG, PNG) or PDF documents</Text2>
|
|
116
116
|
</TextContainer>
|
|
117
117
|
</Container>
|
|
118
118
|
</DraggerContainer>
|
|
@@ -3,6 +3,7 @@ import { useAxios } from '@bjsdistribution/dms-component-library';
|
|
|
3
3
|
import {
|
|
4
4
|
useCurrentAction,
|
|
5
5
|
useCustomerInfo,
|
|
6
|
+
usegetClientData,
|
|
6
7
|
useOrderDetails,
|
|
7
8
|
} from '../../../store';
|
|
8
9
|
import { ApiResponse } from '../../../types';
|
|
@@ -10,12 +11,16 @@ import OrderActionModal from '../../../../../common/modals/order-action-modal/Or
|
|
|
10
11
|
import { Empty, Skeleton } from 'antd';
|
|
11
12
|
|
|
12
13
|
import styled from 'styled-components';
|
|
14
|
+
import { ClientType } from '../../../../order-daily-overview/common/interface';
|
|
15
|
+
import { getBaseUrlSymbol } from '../../../utils';
|
|
13
16
|
|
|
14
17
|
function ChargeLog() {
|
|
15
18
|
const [, setCurrentAction] = useCurrentAction();
|
|
16
19
|
const [customerInfo] = useCustomerInfo();
|
|
17
20
|
const infoText = `${customerInfo?.recipient_name}, ${customerInfo?.postcode}`;
|
|
18
21
|
const [orderDetails] = useOrderDetails();
|
|
22
|
+
const baseUrlSymbol = getBaseUrlSymbol();
|
|
23
|
+
const [clientData] = usegetClientData();
|
|
19
24
|
|
|
20
25
|
const closeModal = () => {
|
|
21
26
|
setCurrentAction(undefined);
|
|
@@ -71,11 +76,31 @@ function ChargeLog() {
|
|
|
71
76
|
fetchedData?.data?.map((order: any) => {
|
|
72
77
|
const label = order?.status?.split(':')[0];
|
|
73
78
|
const val = order?.status?.split(':')[1];
|
|
79
|
+
const baseUrl = clientData?.filter(
|
|
80
|
+
(client: ClientType) => client?.client_id === order?.client_id
|
|
81
|
+
)[0]?.url;
|
|
82
|
+
|
|
83
|
+
const redirectionLink =
|
|
84
|
+
baseUrl + `/finance/order/${order?.debrief_id}`;
|
|
74
85
|
return (
|
|
75
86
|
<tr>
|
|
76
87
|
<td>{order?.date}</td>
|
|
77
88
|
<td>{order?.description}</td>
|
|
78
|
-
<td>
|
|
89
|
+
<td>
|
|
90
|
+
<a
|
|
91
|
+
style={{
|
|
92
|
+
color: '#09F',
|
|
93
|
+
cursor: 'pointer',
|
|
94
|
+
textDecoration: 'none',
|
|
95
|
+
}}
|
|
96
|
+
href={`https://${redirectionLink}`}
|
|
97
|
+
target="_blank"
|
|
98
|
+
rel="noopener noreferrer"
|
|
99
|
+
>
|
|
100
|
+
{baseUrlSymbol}
|
|
101
|
+
{order?.amount}
|
|
102
|
+
</a>
|
|
103
|
+
</td>
|
|
79
104
|
<td>
|
|
80
105
|
<div
|
|
81
106
|
style={{
|
package/src/components/order-view/order-actions/actions/confirm-order/customer-info/DeliveryDate.tsx
CHANGED
|
@@ -55,9 +55,7 @@ function DeliveryDate() {
|
|
|
55
55
|
: null
|
|
56
56
|
}
|
|
57
57
|
style={{ borderRadius: '15px', minWidth: '60px' }}
|
|
58
|
-
disabledDate={
|
|
59
|
-
current && current.isBefore(dayjs().startOf('day'))
|
|
60
|
-
}
|
|
58
|
+
disabledDate={() => false}
|
|
61
59
|
allowClear={false}
|
|
62
60
|
/>
|
|
63
61
|
</Container>
|
package/src/components/order-view/order-actions/actions/create-task-reminder/CreateReminderModal.tsx
CHANGED
|
@@ -228,10 +228,13 @@ function CreateReminderModal() {
|
|
|
228
228
|
}
|
|
229
229
|
}, [fetchData]);
|
|
230
230
|
|
|
231
|
-
const userOptions =
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
231
|
+
const userOptions =
|
|
232
|
+
selectedDeptId?.length > 0
|
|
233
|
+
? filteredUsers?.map((user: any) => ({
|
|
234
|
+
label: `${user.fName} ${user.lName}`,
|
|
235
|
+
value: user.id,
|
|
236
|
+
}))
|
|
237
|
+
: [];
|
|
235
238
|
|
|
236
239
|
const handleAssignToMe = () => {
|
|
237
240
|
const storedUserJSON = localStorage.getItem('user_type');
|
|
@@ -267,7 +267,15 @@ function ForceDebriefModal(props: IProps) {
|
|
|
267
267
|
orderDetails
|
|
268
268
|
);
|
|
269
269
|
|
|
270
|
-
|
|
270
|
+
type ServiceKey =
|
|
271
|
+
| 'assemble'
|
|
272
|
+
| 'disassemble'
|
|
273
|
+
| 'unpack_items'
|
|
274
|
+
| 'collect_disposal'
|
|
275
|
+
| 'take_packaging_away'
|
|
276
|
+
| 'assembly_type';
|
|
277
|
+
|
|
278
|
+
const serviceKeys: ServiceKey[] = [
|
|
271
279
|
'assemble',
|
|
272
280
|
'disassemble',
|
|
273
281
|
'unpack_items',
|
|
@@ -276,28 +284,21 @@ function ForceDebriefModal(props: IProps) {
|
|
|
276
284
|
'assembly_type',
|
|
277
285
|
];
|
|
278
286
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
key in ordersApiPayload
|
|
287
|
-
? ordersApiPayload[key as keyof typeof ordersApiPayload]
|
|
288
|
-
: orderDetails[key as keyof OrdersType];
|
|
289
|
-
delete ordersApiPayload[key as keyof typeof ordersApiPayload];
|
|
290
|
-
if (
|
|
291
|
-
orderServicesPayload?.assemble === false &&
|
|
292
|
-
orderServicesPayload?.disassemble === false
|
|
293
|
-
) {
|
|
294
|
-
orderServicesPayload.assembly_type = null;
|
|
295
|
-
}
|
|
287
|
+
for (const key of serviceKeys) {
|
|
288
|
+
if (key in ordersApiPayload) {
|
|
289
|
+
(ordersApiPayload as Record<ServiceKey, any>)[key] =
|
|
290
|
+
ordersApiPayload[key as keyof typeof ordersApiPayload];
|
|
291
|
+
} else if (key in orderDetails) {
|
|
292
|
+
(ordersApiPayload as Record<ServiceKey, any>)[key] =
|
|
293
|
+
orderDetails[key as keyof OrdersType];
|
|
296
294
|
}
|
|
297
295
|
}
|
|
298
296
|
|
|
299
|
-
if (
|
|
300
|
-
ordersApiPayload
|
|
297
|
+
if (
|
|
298
|
+
ordersApiPayload?.assemble === false &&
|
|
299
|
+
ordersApiPayload?.disassemble === false
|
|
300
|
+
) {
|
|
301
|
+
ordersApiPayload.assembly_type = null;
|
|
301
302
|
}
|
|
302
303
|
|
|
303
304
|
try {
|
|
@@ -1,31 +1,27 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
import { useCurrentAction, useOrderDetails, useOrderId } from '../../../store';
|
|
3
3
|
import OrderActionModal from '../../../../../common/modals/order-action-modal/OrderActionModal';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { Label, StyledInput } from '../modalstyle';
|
|
5
|
+
import { useAxios } from '@bjsdistribution/dms-component-library';
|
|
6
6
|
import { ApiResponse } from '../../../types';
|
|
7
7
|
import styled from 'styled-components';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
width: 700px;
|
|
11
|
-
height: 140px;
|
|
12
|
-
resize: none;
|
|
13
|
-
`;
|
|
14
|
-
|
|
15
|
-
const ErrorText = styled.div`
|
|
16
|
-
color: red;
|
|
17
|
-
font-size: 12px;
|
|
18
|
-
margin-top: 4px;
|
|
19
|
-
`;
|
|
8
|
+
import { notification } from 'antd';
|
|
9
|
+
import TextArea from 'antd/es/input/TextArea';
|
|
20
10
|
|
|
21
11
|
function SendSmsModal() {
|
|
22
12
|
const [, setCurrentAction] = useCurrentAction();
|
|
23
13
|
const [orderDetails] = useOrderDetails();
|
|
24
14
|
const [orderId] = useOrderId();
|
|
25
|
-
const
|
|
15
|
+
const VALID_UK_MOBILE_REGEX = /^(07\d{8,12}|447\d{7,11})$/;
|
|
26
16
|
const [toError, setToError] = useState('');
|
|
27
17
|
const [formData, setFormData] = useState({
|
|
28
|
-
to:
|
|
18
|
+
to: [
|
|
19
|
+
orderDetails?.order_recipient?.contact_mobile,
|
|
20
|
+
orderDetails?.order_recipient?.contact_home,
|
|
21
|
+
orderDetails?.order_recipient?.contact_work,
|
|
22
|
+
]
|
|
23
|
+
.filter(Boolean)
|
|
24
|
+
.join(', '),
|
|
29
25
|
body: '',
|
|
30
26
|
});
|
|
31
27
|
|
|
@@ -42,54 +38,83 @@ function SendSmsModal() {
|
|
|
42
38
|
const { name, value } = e.target;
|
|
43
39
|
|
|
44
40
|
if (name === 'to') {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
41
|
+
const rawValue = value;
|
|
42
|
+
const numbers = rawValue.split(',').map(num => num.trim());
|
|
43
|
+
const invalids = numbers.filter(num => !VALID_UK_MOBILE_REGEX.test(num));
|
|
44
|
+
|
|
45
|
+
if (invalids.length === numbers.length) {
|
|
46
|
+
setToError('Enter at least one valid UK mobile number.');
|
|
47
|
+
} else {
|
|
48
|
+
setToError('');
|
|
52
49
|
}
|
|
53
|
-
|
|
54
|
-
setToError(error);
|
|
55
|
-
|
|
56
|
-
setFormData(prev => ({
|
|
57
|
-
...prev,
|
|
58
|
-
to: digitsOnly.slice(0, 10),
|
|
59
|
-
}));
|
|
50
|
+
setFormData(prev => ({ ...prev, to: rawValue }));
|
|
60
51
|
} else {
|
|
61
|
-
setFormData(prev => ({
|
|
62
|
-
...prev,
|
|
63
|
-
[name]: value,
|
|
64
|
-
}));
|
|
52
|
+
setFormData(prev => ({ ...prev, [name]: value }));
|
|
65
53
|
}
|
|
66
54
|
};
|
|
67
55
|
|
|
68
56
|
const handleSubmit = async () => {
|
|
69
|
-
const trimmedTo = formData?.to?.trim();
|
|
70
57
|
const trimmedBody = formData?.body?.trim();
|
|
58
|
+
if (!trimmedBody) return;
|
|
71
59
|
|
|
72
|
-
|
|
73
|
-
setToError('Phone number must be exactly 10 digits');
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
60
|
+
const rawContacts = formData.to.split(',').map(num => num.trim());
|
|
76
61
|
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
message: trimmedBody,
|
|
81
|
-
notification_type: 'Send SMS',
|
|
82
|
-
};
|
|
62
|
+
const validContacts = rawContacts.filter(num =>
|
|
63
|
+
VALID_UK_MOBILE_REGEX.test(num)
|
|
64
|
+
);
|
|
83
65
|
|
|
84
|
-
if (
|
|
66
|
+
if (validContacts.length === 0) {
|
|
67
|
+
notification.warning({
|
|
68
|
+
message: 'No Valid UK Mobile Numbers',
|
|
69
|
+
description:
|
|
70
|
+
'Please provide at least one valid UK mobile number (e.g., 07123...).',
|
|
71
|
+
});
|
|
85
72
|
return;
|
|
86
73
|
}
|
|
87
74
|
|
|
88
75
|
try {
|
|
89
|
-
|
|
76
|
+
for (const number of validContacts) {
|
|
77
|
+
const payload = {
|
|
78
|
+
order_id: +orderId,
|
|
79
|
+
receiver: number,
|
|
80
|
+
message: trimmedBody,
|
|
81
|
+
notification_type: 'Send SMS',
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const response = (await callSendSms({ payload })) as {
|
|
85
|
+
code: number | null;
|
|
86
|
+
error?: string | null;
|
|
87
|
+
message?: string;
|
|
88
|
+
[key: string]: any;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
if (response.code === 200) {
|
|
92
|
+
notification.success({
|
|
93
|
+
message: 'SMS Sent',
|
|
94
|
+
description: `Message sent to ${number}`,
|
|
95
|
+
});
|
|
96
|
+
} else {
|
|
97
|
+
notification.error({
|
|
98
|
+
message: 'SMS Failed',
|
|
99
|
+
description: `Failed to send to ${number}: ${response?.error ||
|
|
100
|
+
'Unknown error'}`,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
90
105
|
closeModal();
|
|
91
106
|
} catch (error) {
|
|
92
|
-
|
|
107
|
+
const err = error as any;
|
|
108
|
+
const errMsg =
|
|
109
|
+
err?.response?.data?.error ||
|
|
110
|
+
err?.response?.data?.message ||
|
|
111
|
+
err?.message ||
|
|
112
|
+
'An unexpected error occurred.';
|
|
113
|
+
|
|
114
|
+
notification.error({
|
|
115
|
+
message: 'Request Failed',
|
|
116
|
+
description: errMsg,
|
|
117
|
+
});
|
|
93
118
|
}
|
|
94
119
|
};
|
|
95
120
|
|
|
@@ -98,7 +123,7 @@ function SendSmsModal() {
|
|
|
98
123
|
};
|
|
99
124
|
|
|
100
125
|
const isFormValid =
|
|
101
|
-
formData?.to.trim()?.length
|
|
126
|
+
formData?.to.trim()?.length > 0 &&
|
|
102
127
|
!toError &&
|
|
103
128
|
formData?.body.trim().length > 0;
|
|
104
129
|
|
|
@@ -119,21 +144,49 @@ function SendSmsModal() {
|
|
|
119
144
|
onChange={handleInputChange}
|
|
120
145
|
required
|
|
121
146
|
placeholder="Enter the number"
|
|
122
|
-
maxLength={10}
|
|
123
147
|
/>
|
|
124
148
|
{toError && <ErrorText>{toError}</ErrorText>}
|
|
125
149
|
|
|
126
150
|
<Label>Body</Label>
|
|
127
|
-
<
|
|
151
|
+
<TextArea
|
|
152
|
+
style={{ width: '700px', height: '140px', resize: 'none' }}
|
|
128
153
|
name="body"
|
|
129
154
|
value={formData.body}
|
|
130
155
|
onChange={handleInputChange}
|
|
131
156
|
required
|
|
132
157
|
placeholder="Write here..."
|
|
158
|
+
maxLength={500}
|
|
159
|
+
count={{
|
|
160
|
+
show: true,
|
|
161
|
+
max: 500,
|
|
162
|
+
}}
|
|
133
163
|
/>
|
|
134
164
|
</ContentContainer>
|
|
135
165
|
</OrderActionModal>
|
|
136
166
|
);
|
|
137
167
|
}
|
|
138
168
|
|
|
169
|
+
export const ContentContainer = styled.div`
|
|
170
|
+
display: flex;
|
|
171
|
+
padding: 20px;
|
|
172
|
+
flex-direction: column;
|
|
173
|
+
justify-content: center;
|
|
174
|
+
align-items: flex-start;
|
|
175
|
+
gap: 15px;
|
|
176
|
+
flex: 1 0 0;
|
|
177
|
+
border-radius: 15px;
|
|
178
|
+
`;
|
|
179
|
+
|
|
180
|
+
// const CustomTextArea = styled(TextArea)`
|
|
181
|
+
// width: 700px;
|
|
182
|
+
// height: 140px;
|
|
183
|
+
// resize: none;
|
|
184
|
+
// `;
|
|
185
|
+
|
|
186
|
+
const ErrorText = styled.div`
|
|
187
|
+
color: red;
|
|
188
|
+
font-size: 12px;
|
|
189
|
+
margin-top: 4px;
|
|
190
|
+
`;
|
|
191
|
+
|
|
139
192
|
export default SendSmsModal;
|