@bytebrand/fe-ui-core 4.2.58 → 4.2.60
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/__tests__/components/VehicleSmallCard/VehicleInfo.test.tsx +88 -0
- package/__tests__/components/VehicleSmallCard/VehicleTitle.test.tsx +91 -0
- package/__tests__/components/_common/IconSVG/IconSVG.test.tsx +23 -0
- package/__tests__/components/_common/StarButton/StarButton.test.tsx +46 -0
- package/common.ts +3 -0
- package/package.json +1 -1
- package/source/components/UserDashboardPage/sections/OrderStatusSection/AdditionalOrderInfo.styl +17 -5
- package/source/components/UserDashboardPage/sections/OrderStatusSection/AdditionalOrderInfo.tsx +25 -31
- package/source/components/UserDashboardPage/sections/OrderStatusSection/OrderStatusSection.tsx +49 -41
- package/source/framework/factories/BreadcrumbsFactory.tsx +7 -0
- package/source/framework/types/types.ts +4 -2
- package/source/framework/utils/CommonUtils.ts +2 -2
- package/source/components/Vehicle/VehicleTitle/VehicleTitle.story.js +0 -27
- package/source/components/Vehicle/VehicleTitle/VehicleTitle.styl +0 -48
- package/source/components/Vehicle/VehicleTitle/VehicleTitle.tsx +0 -44
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { fireEvent, render } from '@testing-library/react';
|
|
3
|
+
import VehicleInfo from '../../../source/components/VehicleSmallCard/VehicleData/VehicleInfo/VehicleInfo';
|
|
4
|
+
import { IVehicleInfo } from '../../../source/framework/types/types';
|
|
5
|
+
|
|
6
|
+
const defaultProps: IVehicleInfo = {
|
|
7
|
+
t: jest.fn(), // mock translation function
|
|
8
|
+
mlCurrentSalesPricePredicted: 30000,
|
|
9
|
+
i18nPrefixForPriceRating: 'common:',
|
|
10
|
+
vehicleComponentName: 'search',
|
|
11
|
+
combineRefAlternative: false,
|
|
12
|
+
language: 'en',
|
|
13
|
+
url: '/vehicle',
|
|
14
|
+
target: '_self',
|
|
15
|
+
rel: 'noopener noreferrer',
|
|
16
|
+
regDate: 20210101,
|
|
17
|
+
linkTag: 'a',
|
|
18
|
+
routeObj: {},
|
|
19
|
+
gearbox: 'Automatic',
|
|
20
|
+
numberOfPreviousOwners: 2,
|
|
21
|
+
usageType: 'Used',
|
|
22
|
+
condition: 'Good',
|
|
23
|
+
damaged: false,
|
|
24
|
+
driveType: 'AWD',
|
|
25
|
+
environmentEmissions: { co2: 123 },
|
|
26
|
+
common: {
|
|
27
|
+
currentSalesPrice: 235,
|
|
28
|
+
mileage: '7.5'
|
|
29
|
+
},
|
|
30
|
+
consumption: {
|
|
31
|
+
fuel: 'Gasoline',
|
|
32
|
+
consumptionCombined: 7.5,
|
|
33
|
+
consumptionPowerCombined: 9.1,
|
|
34
|
+
},
|
|
35
|
+
location: {
|
|
36
|
+
city: 'Lviv',
|
|
37
|
+
street: 'street',
|
|
38
|
+
zipCode: 55555
|
|
39
|
+
},
|
|
40
|
+
offer: 'Offer',
|
|
41
|
+
engineData: {
|
|
42
|
+
power: '200hp',
|
|
43
|
+
cylinder: '8',
|
|
44
|
+
fuelType: 'Gasoline',
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
describe('VehicleInfo', () => {
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
jest.clearAllMocks();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('renders without crashing with default props', () => {
|
|
54
|
+
render(<VehicleInfo {...defaultProps} />);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('renders the link with the correct href', () => {
|
|
58
|
+
const { getByRole } = render(<VehicleInfo {...defaultProps} url="/vehicles/123" />);
|
|
59
|
+
expect(getByRole('link')).toHaveAttribute('href', '/vehicles/123');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// it('renders the correct main properties for comparable cars', () => {
|
|
63
|
+
// const { getByText } = render(<VehicleInfo {...defaultProps} vehicleComponentName="comparable" />);
|
|
64
|
+
// expect(getByText('Fuel Type')).toBeInTheDocument();
|
|
65
|
+
// expect(getByText('Consumption Combined (l/100km)')).toBeInTheDocument();
|
|
66
|
+
// expect(getByText('Power')).toBeInTheDocument();
|
|
67
|
+
// });
|
|
68
|
+
|
|
69
|
+
// it('renders the correct main properties for landing cars', () => {
|
|
70
|
+
// const { getByText } = render(<VehicleInfo {...defaultProps} vehicleComponentName="landing" />);
|
|
71
|
+
// expect(getByText('Mileage')).toBeInTheDocument();
|
|
72
|
+
// expect(getByText('First Registration')).toBeInTheDocument();
|
|
73
|
+
// expect(getByText('Fuel Type')).toBeInTheDocument();
|
|
74
|
+
// });
|
|
75
|
+
|
|
76
|
+
// it('renders the correct main properties for SRL', () => {
|
|
77
|
+
// const { getByText } = render(<VehicleInfo {...defaultProps} vehicleComponentName="myVehicles" />);
|
|
78
|
+
// expect(getByText('Mileage')).toBeInTheDocument();
|
|
79
|
+
// expect(getByText('First Registration')).toBeInTheDocument();
|
|
80
|
+
// expect(getByText('Drive type')).toBeInTheDocument();
|
|
81
|
+
// });
|
|
82
|
+
|
|
83
|
+
// it('renders the correct PriceRating component', () => {
|
|
84
|
+
// const { getByTestId } = render(<VehicleInfo {...defaultProps} />);
|
|
85
|
+
// expect(getByTestId('price-rating')).toBeInTheDocument();
|
|
86
|
+
// });
|
|
87
|
+
|
|
88
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { fireEvent, render } from '@testing-library/react';
|
|
3
|
+
import VehicleTitle from '../../../source/components/VehicleSmallCard/VehicleData/VehicleTitle/VehicleTitle';
|
|
4
|
+
import { IVehicleTitleProps } from '../../../source/framework/types/types';
|
|
5
|
+
|
|
6
|
+
const props: IVehicleTitleProps = {
|
|
7
|
+
onFavoriteClick: jest.fn(),
|
|
8
|
+
isCarInFavorite: false,
|
|
9
|
+
id: '123',
|
|
10
|
+
make: 'Toyota',
|
|
11
|
+
model: 'Camry',
|
|
12
|
+
subModel: 'subModelText',
|
|
13
|
+
option: 'option',
|
|
14
|
+
isSponsored: true,
|
|
15
|
+
combineRefAlternative: true,
|
|
16
|
+
vehicleComponentName: 'search',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
describe('VehicleTitle', () => {
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
jest.clearAllMocks();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should render VehicleTitle component with correct texts and styles', () => {
|
|
25
|
+
const { container, getByText } = render(<VehicleTitle {...props} />);
|
|
26
|
+
|
|
27
|
+
const infoSectionElement = container.querySelector('.infoSection');
|
|
28
|
+
const wrapTitlesElement = container.querySelector('.wrapTitles');
|
|
29
|
+
const optionTitleElement = container.querySelector('.optionTitle');
|
|
30
|
+
|
|
31
|
+
switch (props.vehicleComponentName) {
|
|
32
|
+
case 'search':
|
|
33
|
+
expect(infoSectionElement).toHaveClass('infoSectionSearch');
|
|
34
|
+
expect(wrapTitlesElement).toHaveClass('wrapTitlesSearch');
|
|
35
|
+
expect(optionTitleElement).toHaveClass('optionTitleSearch');
|
|
36
|
+
break;
|
|
37
|
+
case 'recently':
|
|
38
|
+
expect(infoSectionElement).toHaveClass('infoSectionRecently');
|
|
39
|
+
expect(wrapTitlesElement).toHaveClass('wrapTitlesRecently');
|
|
40
|
+
expect(optionTitleElement).toHaveClass('optionTitleRecently');
|
|
41
|
+
break;
|
|
42
|
+
case 'favorite':
|
|
43
|
+
expect(infoSectionElement).toHaveClass('infoSectionRecently');
|
|
44
|
+
expect(wrapTitlesElement).toHaveClass('wrapTitlesFavorite');
|
|
45
|
+
break;
|
|
46
|
+
case 'landing':
|
|
47
|
+
expect(infoSectionElement).toHaveClass('infoSectionRecently');
|
|
48
|
+
expect(wrapTitlesElement).toHaveClass('wrapTitlesRecently');
|
|
49
|
+
expect(optionTitleElement).toHaveClass('optionTitleRecently');
|
|
50
|
+
break;
|
|
51
|
+
case 'myVehicles':
|
|
52
|
+
expect(infoSectionElement).toHaveClass('infoSectionMyVehicles');
|
|
53
|
+
expect(wrapTitlesElement).toHaveClass('wrapTitlesSearch');
|
|
54
|
+
expect(optionTitleElement).toHaveClass('optionTitleSearch');
|
|
55
|
+
break;
|
|
56
|
+
case 'main':
|
|
57
|
+
expect(infoSectionElement).toHaveClass('infoSectionRecently');
|
|
58
|
+
expect(wrapTitlesElement).toHaveClass('wrapTitlesRecently');
|
|
59
|
+
expect(optionTitleElement).toHaveClass('optionTitleRecently');
|
|
60
|
+
break;
|
|
61
|
+
default:
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (props.combineRefAlternative) {
|
|
66
|
+
expect(optionTitleElement).toHaveClass('optionTitleIsAlternative');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
expect(getByText('Toyota Camry')).toBeInTheDocument();
|
|
70
|
+
expect(getByText('subModelText')).toBeInTheDocument();
|
|
71
|
+
expect(getByText('option')).toBeInTheDocument();
|
|
72
|
+
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('render StarButton when combineRefAlternative is false', () => {
|
|
76
|
+
const { container } = render(<VehicleTitle {...props} combineRefAlternative={false} showFavoriteStar={true} />);
|
|
77
|
+
expect(container.querySelector('.starButton')).toBeInTheDocument();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should call onFavoriteClick handler when clicking on StarButton', () => {
|
|
81
|
+
|
|
82
|
+
const { container } = render(<VehicleTitle {...props} combineRefAlternative={false} showFavoriteStar={true} />);
|
|
83
|
+
|
|
84
|
+
fireEvent.click(container.querySelector('svg'));
|
|
85
|
+
expect(props.onFavoriteClick).toHaveBeenCalledTimes(1);
|
|
86
|
+
expect(props.onFavoriteClick).toHaveBeenCalledWith(
|
|
87
|
+
expect.objectContaining({ type: 'click' }),
|
|
88
|
+
props.id
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import IconSVG from '../../../../source/components/_common/IconSVG/IconSVG';
|
|
4
|
+
|
|
5
|
+
describe('IconSVG component', () => {
|
|
6
|
+
it('should render the SVG icon when a valid name prop is passed', () => {
|
|
7
|
+
const { container } = render(<IconSVG name='Search' />);
|
|
8
|
+
const svgIcon = container.querySelector('svg');
|
|
9
|
+
expect(svgIcon).toBeDefined();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should not render anything when an invalid name prop is passed', () => {
|
|
13
|
+
const { container } = render(<IconSVG name='nonexistenticon' />);
|
|
14
|
+
const svgIcon = container.querySelector('svg');
|
|
15
|
+
expect(svgIcon).toBeNull();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should apply custom dimensions when customDimensions prop is false', () => {
|
|
19
|
+
const { container } = render(<IconSVG name="search" customDimensions={false} />);
|
|
20
|
+
const svgIcon = container.querySelector('svg');
|
|
21
|
+
expect(svgIcon).toHaveAttribute('style', 'width: 1.5rem; height: auto;');
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, fireEvent } from '@testing-library/react';
|
|
3
|
+
import StarButton from '../../../../source/components/_common/StarButton/StarButton';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
describe('StarButton', () => {
|
|
7
|
+
const defaultProps = {
|
|
8
|
+
onClick: jest.fn(),
|
|
9
|
+
isFavorite: true,
|
|
10
|
+
amountOfFavorite: 5
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
it('render StarButton component', () => {
|
|
14
|
+
const { container } = render(<StarButton {...defaultProps} />);
|
|
15
|
+
const svgIcon = container.querySelector('svg');
|
|
16
|
+
expect(svgIcon.firstChild).toBeTruthy();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should trigger the onClick function when clicked', () => {
|
|
20
|
+
const { container } = render(<StarButton {...defaultProps} />);
|
|
21
|
+
fireEvent.click(container.querySelector('svg'));
|
|
22
|
+
expect(defaultProps.onClick).toHaveBeenCalled();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('render amount of favorite if is amountOfFavorite are true', () => {
|
|
26
|
+
const { container, queryByTitle } = render(<StarButton {...defaultProps} amountOfFavorite={5} />);
|
|
27
|
+
expect(container.querySelector('span')).toBeInTheDocument();
|
|
28
|
+
expect(queryByTitle(`${defaultProps.amountOfFavorite}`));
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('removes amount of favorite if no favorites are present', () => {
|
|
32
|
+
const { queryByTitle } = render(<StarButton {...defaultProps} amountOfFavorite={0} />);
|
|
33
|
+
expect(queryByTitle(`${defaultProps.amountOfFavorite}`)).toBeFalsy();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('renders custom container class when custom container prop exists', () => {
|
|
37
|
+
const customContainerClass = 'custom-container-class';
|
|
38
|
+
const { container } = render(<StarButton {...defaultProps} customContainer={customContainerClass} />);
|
|
39
|
+
expect(container.firstChild).toHaveClass(customContainerClass);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('adds "active" className when isFavorite is true', () => {
|
|
43
|
+
const { container } = render(<StarButton isFavorite />);
|
|
44
|
+
expect(container.querySelector('svg')).toHaveClass('active');
|
|
45
|
+
});
|
|
46
|
+
});
|
package/common.ts
CHANGED
|
@@ -85,6 +85,9 @@ export { default as Breadcrumbs } from './source/components/Breadcrumbs/Breadcru
|
|
|
85
85
|
export { default as FirstInfoBlock } from './source/components/Breadcrumbs/FirstInfoBlock/FirstInfoBlock';
|
|
86
86
|
export { default as InfoBlockWrapper } from './source/components/InfoBlocks/InfoBlockWrapper/InfoBlockWrapper';
|
|
87
87
|
|
|
88
|
+
// USER DASHBOARD
|
|
89
|
+
export { default as OrderStatusSection } from './source/components/UserDashboardPage/sections/OrderStatusSection/OrderStatusSection';
|
|
90
|
+
|
|
88
91
|
// Filters
|
|
89
92
|
export { default as FilterBlock } from './source/components/SearchFilters/common/FilterBlock/FilterBlock';
|
|
90
93
|
|
package/package.json
CHANGED
package/source/components/UserDashboardPage/sections/OrderStatusSection/AdditionalOrderInfo.styl
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
@import '
|
|
2
|
-
@import '
|
|
1
|
+
@import '../../../../../theme/theme.styl'
|
|
2
|
+
@import '../../../../../theme/mixins.styl'
|
|
3
3
|
|
|
4
4
|
.container
|
|
5
5
|
padding: 8px 8px 16px
|
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
+media-tablet-landscape-up()
|
|
8
8
|
padding: 12px 16px
|
|
9
9
|
display: grid
|
|
10
|
+
grid-template-areas:
|
|
11
|
+
'addresses orderPrice'\
|
|
12
|
+
'customerSuport orderPrice'
|
|
10
13
|
grid-template-columns: 1fr 1fr
|
|
11
14
|
column-gap: 32px
|
|
12
15
|
|
|
@@ -14,7 +17,7 @@
|
|
|
14
17
|
.orderInfoSection:first-child
|
|
15
18
|
position: relative
|
|
16
19
|
|
|
17
|
-
.
|
|
20
|
+
.orderPriceSection:before
|
|
18
21
|
content: ''
|
|
19
22
|
width: 1px
|
|
20
23
|
height: 100%
|
|
@@ -22,7 +25,7 @@
|
|
|
22
25
|
display: block;
|
|
23
26
|
position: absolute;
|
|
24
27
|
top: 0;
|
|
25
|
-
|
|
28
|
+
left: -16px;
|
|
26
29
|
|
|
27
30
|
.labelText
|
|
28
31
|
color: $black50
|
|
@@ -31,9 +34,18 @@
|
|
|
31
34
|
|
|
32
35
|
.orderInfoAddresses
|
|
33
36
|
padding-bottom: 8px
|
|
37
|
+
grid-area: addresses
|
|
38
|
+
display: grid
|
|
39
|
+
|
|
40
|
+
.orderPriceSection
|
|
41
|
+
grid-area: orderPrice
|
|
42
|
+
+media-tablet-landscape-up()
|
|
43
|
+
position: relative
|
|
34
44
|
|
|
35
45
|
.orderInfoPrice
|
|
36
46
|
border-top: 1px solid rgba(76,78,100,0.12)
|
|
47
|
+
+media-tablet-landscape-up()
|
|
48
|
+
border: none
|
|
37
49
|
|
|
38
50
|
.overallRateLabel
|
|
39
51
|
@extend .labelText
|
|
@@ -48,9 +60,9 @@
|
|
|
48
60
|
.customerSuport
|
|
49
61
|
@extend .labelText
|
|
50
62
|
text-align: center
|
|
51
|
-
margin-top: 8px
|
|
52
63
|
padding: 16px 24px
|
|
53
64
|
border-top: 1px solid rgba(76,78,100,0.12)
|
|
65
|
+
grid-area: customerSuport
|
|
54
66
|
|
|
55
67
|
.customerSuportButton
|
|
56
68
|
margin-top: 8px
|
package/source/components/UserDashboardPage/sections/OrderStatusSection/AdditionalOrderInfo.tsx
CHANGED
|
@@ -9,8 +9,7 @@ import classNames from 'classnames';
|
|
|
9
9
|
|
|
10
10
|
interface IAdditionalOrderData {
|
|
11
11
|
addressData: { label: string, value: string }[];
|
|
12
|
-
carPriceData: { label: string, value: string }[];
|
|
13
|
-
financingData: { label: string, value: string }[];
|
|
12
|
+
carPriceData: { label: string, value: string | number }[];
|
|
14
13
|
overallRate: {
|
|
15
14
|
label: string,
|
|
16
15
|
value: number
|
|
@@ -20,24 +19,30 @@ interface IAdditionalOrderData {
|
|
|
20
19
|
interface IAdditionalOrderInfo {
|
|
21
20
|
t: i18n.TFunction;
|
|
22
21
|
additionalOrderData: IAdditionalOrderData;
|
|
22
|
+
redirectToUrl: (url: string) => void;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const AdditionalOrderInfo = ({ t, additionalOrderData }: IAdditionalOrderInfo) => {
|
|
26
|
-
const { addressData, carPriceData,
|
|
25
|
+
const AdditionalOrderInfo = ({ t, additionalOrderData, redirectToUrl }: IAdditionalOrderInfo) => {
|
|
26
|
+
const { addressData, carPriceData, overallRate } = additionalOrderData;
|
|
27
|
+
|
|
28
|
+
const redirectToContactPage = () => {
|
|
29
|
+
return redirectToUrl('/account/kontakt');
|
|
30
|
+
};
|
|
31
|
+
|
|
27
32
|
return (
|
|
28
33
|
<div className={styles.container}>
|
|
29
|
-
<div className={styles.
|
|
30
|
-
|
|
31
|
-
{
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
<div
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
<div className={styles.orderInfoAddresses}>
|
|
35
|
+
{addressData.map((data) => {
|
|
36
|
+
const { value, label } = data;
|
|
37
|
+
return (
|
|
38
|
+
<div key={label}>
|
|
39
|
+
<div className={styles.labelText}>{label}</div>
|
|
40
|
+
<div className={styles.labelText}>{value}</div>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
})}
|
|
44
|
+
</div>
|
|
45
|
+
<div className={styles.orderPriceSection}>
|
|
41
46
|
<div className={styles.orderInfoPrice}>
|
|
42
47
|
{carPriceData.map((data) => {
|
|
43
48
|
const { value, label } = data;
|
|
@@ -49,18 +54,7 @@ const AdditionalOrderInfo = ({ t, additionalOrderData }: IAdditionalOrderInfo) =
|
|
|
49
54
|
);
|
|
50
55
|
})}
|
|
51
56
|
</div>
|
|
52
|
-
</div>
|
|
53
|
-
<div className={styles.orderInfoSection}>
|
|
54
57
|
<div className={styles.orderInfoFinancing}>
|
|
55
|
-
{financingData.map((data) => {
|
|
56
|
-
const { value, label } = data;
|
|
57
|
-
return (
|
|
58
|
-
<div className={styles.flexContainer} key={label}>
|
|
59
|
-
<div className={styles.labelText}>{label}</div>
|
|
60
|
-
<div className={styles.labelText}>{value}</div>
|
|
61
|
-
</div>
|
|
62
|
-
);
|
|
63
|
-
})}
|
|
64
58
|
<div className={classNames(styles.flexContainer, styles.overallRate)}>
|
|
65
59
|
<div className={styles.overallRateLabel}>{overallRate.label}</div>
|
|
66
60
|
<div>
|
|
@@ -77,10 +71,10 @@ const AdditionalOrderInfo = ({ t, additionalOrderData }: IAdditionalOrderInfo) =
|
|
|
77
71
|
</div>
|
|
78
72
|
</div>
|
|
79
73
|
</div>
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
</
|
|
74
|
+
</div>
|
|
75
|
+
<div className={styles.customerSuport}>
|
|
76
|
+
<div>{t('customerSupportText')}</div>
|
|
77
|
+
<Button onClick={redirectToContactPage} className={styles.customerSuportButton} variant='outlined'>{t('customerSupport')}</Button>
|
|
84
78
|
</div>
|
|
85
79
|
</div>
|
|
86
80
|
);
|
package/source/components/UserDashboardPage/sections/OrderStatusSection/OrderStatusSection.tsx
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
3
|
import DashboardSection from '../../../containers/DasboardSection/DashboardSection';
|
|
4
|
-
|
|
4
|
+
import AdditionalOrderInfo from './AdditionalOrderInfo';
|
|
5
5
|
import OrderStatusCar from './OrderStatusCar';
|
|
6
6
|
import styles from './OrderStatusSection.styl';
|
|
7
|
-
// import { history } from '../../../../../AppRouter';
|
|
8
7
|
import { Skeleton } from '@mui/material';
|
|
9
|
-
|
|
8
|
+
|
|
9
|
+
import { getFormattedPrice } from '../../../../framework/utils/CommonUtils';
|
|
10
|
+
|
|
11
|
+
import i18n from 'i18next';
|
|
10
12
|
|
|
11
13
|
interface IOrderStatusSection {
|
|
12
|
-
redirectToCar: (id: string) => void;
|
|
13
14
|
getSupportedImageFormat: (id: string, mainImageId: string, size: 'small' | 'medium' | 'large') => string;
|
|
14
|
-
|
|
15
|
+
redirectToUrl: (url: string) => void;
|
|
16
|
+
t: i18n.TFunction;
|
|
15
17
|
isFetching: boolean;
|
|
16
18
|
withAdditionalInfo?: boolean; // for using on /account/my-order.
|
|
17
19
|
orderedCars: any[]; // TODO: create interface for orderedCars, when the object is formatted by the backend
|
|
@@ -20,13 +22,14 @@ interface IOrderStatusSection {
|
|
|
20
22
|
const OrderStatusSection = ({
|
|
21
23
|
getSupportedImageFormat,
|
|
22
24
|
t,
|
|
25
|
+
redirectToUrl,
|
|
23
26
|
orderedCars,
|
|
24
27
|
isFetching,
|
|
25
|
-
withAdditionalInfo = false
|
|
26
|
-
redirectToCar
|
|
28
|
+
withAdditionalInfo = false
|
|
27
29
|
}: IOrderStatusSection) => {
|
|
30
|
+
|
|
28
31
|
const onDetailsClick = (carId: string) => {
|
|
29
|
-
|
|
32
|
+
return redirectToUrl(`/search/vehicle/${carId}`);
|
|
30
33
|
};
|
|
31
34
|
|
|
32
35
|
const renderRequestedCars = () => {
|
|
@@ -35,7 +38,7 @@ const OrderStatusSection = ({
|
|
|
35
38
|
{!!orderedCars && orderedCars.map((orderedCar, index) => {
|
|
36
39
|
const { car: { _id } } = orderedCar;
|
|
37
40
|
const { car: { mainData: { make, model, subModel } } } = orderedCar;
|
|
38
|
-
const { car: { metaData: { mainImageId } }
|
|
41
|
+
const { car: { metaData: { mainImageId } }} = orderedCar;
|
|
39
42
|
const {
|
|
40
43
|
buyingType,
|
|
41
44
|
selfPickup,
|
|
@@ -44,39 +47,44 @@ const OrderStatusSection = ({
|
|
|
44
47
|
status,
|
|
45
48
|
request,
|
|
46
49
|
registration,
|
|
47
|
-
currentSalesPrice
|
|
50
|
+
currentSalesPrice,
|
|
51
|
+
deposit,
|
|
52
|
+
preparationCost,
|
|
53
|
+
licensePlateCost,
|
|
54
|
+
registrationCost,
|
|
55
|
+
transportationCost,
|
|
56
|
+
customerAddress: {
|
|
57
|
+
customerFirstname,
|
|
58
|
+
customerLastname
|
|
59
|
+
}
|
|
48
60
|
} = orderedCar;
|
|
49
61
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// overallRate: {
|
|
71
|
-
// label: 'Your overall rate',
|
|
72
|
-
// value: 599
|
|
73
|
-
// }
|
|
74
|
-
// };
|
|
62
|
+
const additionalOrderData = {
|
|
63
|
+
addressData: [
|
|
64
|
+
{ label: 'Billing address:', value: `${customerFirstname} ${customerLastname}` },
|
|
65
|
+
{ label: 'Vehicle delivery:', value: 'Hannes Käther, Wittenberger Str. 81, 04129 Leipzig' }
|
|
66
|
+
],
|
|
67
|
+
carPriceData: [
|
|
68
|
+
{ label: 'Car price', value: getFormattedPrice(currentSalesPrice, '$,.2f', ' €') },
|
|
69
|
+
{ label: 'Preparation cost', value: getFormattedPrice(preparationCost, '$,.2f', ' €')},
|
|
70
|
+
{ label: 'vehicle registration', value: getFormattedPrice(registrationCost, '$,.2f', ' €') },
|
|
71
|
+
{ label: 'license plates', value: getFormattedPrice(licensePlateCost, '$,.2f', ' €') },
|
|
72
|
+
{ label: 'Delivery', value: getFormattedPrice(transportationCost, '$,.2f', ' €') },
|
|
73
|
+
{ label: `${buyingType} rate`, value: buyingType !== 'buy' ? `${getFormattedPrice(monthlyInstallment, '$,.2f', '', '€')} per month` : getFormattedPrice(monthlyInstallment, '$,.2f', ' €') },
|
|
74
|
+
{ label: 'Running time (months)', value: `${paybackPeriod} months` },
|
|
75
|
+
{ label: 'Deposit', value: getFormattedPrice(deposit, '$,.2f', ' €') }
|
|
76
|
+
],
|
|
77
|
+
overallRate: {
|
|
78
|
+
label: 'Your overall rate',
|
|
79
|
+
value: monthlyInstallment
|
|
80
|
+
}
|
|
81
|
+
};
|
|
75
82
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
83
|
+
const additionalOrderInfoProps = {
|
|
84
|
+
t,
|
|
85
|
+
additionalOrderData,
|
|
86
|
+
redirectToUrl
|
|
87
|
+
};
|
|
80
88
|
|
|
81
89
|
const imageUrl = getSupportedImageFormat(_id, mainImageId, 'small');
|
|
82
90
|
|
|
@@ -105,7 +113,7 @@ const OrderStatusSection = ({
|
|
|
105
113
|
return (
|
|
106
114
|
<DashboardSection key={index} title={title}>
|
|
107
115
|
<OrderStatusCar { ...orderStatusCarProps } />
|
|
108
|
-
{
|
|
116
|
+
{withAdditionalInfo ? <AdditionalOrderInfo { ...additionalOrderInfoProps } /> : null}
|
|
109
117
|
</DashboardSection>
|
|
110
118
|
);
|
|
111
119
|
})}
|
|
@@ -115,7 +123,7 @@ const OrderStatusSection = ({
|
|
|
115
123
|
|
|
116
124
|
return (
|
|
117
125
|
<>
|
|
118
|
-
{isFetching ?
|
|
126
|
+
{isFetching ?
|
|
119
127
|
<div className={styles.skeletonBlock}>
|
|
120
128
|
<Skeleton animation='wave' variant='rectangular' width='100%' />
|
|
121
129
|
</div>
|
|
@@ -59,6 +59,13 @@ const BreadcrumbsFactory: IBreadcrumbsFactoryConfig = {
|
|
|
59
59
|
className: 'breadcrumbsUserDashboard'
|
|
60
60
|
}),
|
|
61
61
|
|
|
62
|
+
MY_ORDER: (props: any) => ({
|
|
63
|
+
list: [
|
|
64
|
+
{ link: '/account/dashboard', title: props.t('common:breadcrumbs.DashboardNew') },
|
|
65
|
+
{ link: '', title: props.t('common:breadcrumbs.myOrder') }
|
|
66
|
+
]
|
|
67
|
+
}),
|
|
68
|
+
|
|
62
69
|
DEALER_EMPLOYEES_SET: (props: any) => ({
|
|
63
70
|
list: [
|
|
64
71
|
{ link: '/account/position/employees', title: props.t('common:breadcrumbs.Employees') },
|
|
@@ -95,9 +95,11 @@ export interface IVehicleInfo {
|
|
|
95
95
|
routeObj?: any;
|
|
96
96
|
linkTag?: string;
|
|
97
97
|
mlCurrentSalesPricePredicted?: number;
|
|
98
|
-
currentSalesPrice?: number;
|
|
99
|
-
mileage?: string;
|
|
100
98
|
location?: IVehicleLocation;
|
|
99
|
+
common: {
|
|
100
|
+
currentSalesPrice?: number;
|
|
101
|
+
mileage?: string;
|
|
102
|
+
};
|
|
101
103
|
environmentEmissions?: any;
|
|
102
104
|
engineData?: any;
|
|
103
105
|
offer?: any;
|
|
@@ -311,13 +311,13 @@ export const sliceMoreThan = (array: number[], from: any): number[] => {
|
|
|
311
311
|
: array;
|
|
312
312
|
};
|
|
313
313
|
|
|
314
|
-
export const getFormattedPrice = (price: number, format = '$,.0f', currency = ''): string => {
|
|
314
|
+
export const getFormattedPrice = (price: number, format = '$,.0f', currency = '', currencyPrefix = ''): string => {
|
|
315
315
|
if (!Number.isFinite(price)) return '-';
|
|
316
316
|
const groupingNum = 3;
|
|
317
317
|
return d3formatLocale({
|
|
318
318
|
decimal: ',',
|
|
319
319
|
thousands: '.',
|
|
320
|
-
currency: [
|
|
320
|
+
currency: [currencyPrefix, currency],
|
|
321
321
|
grouping: [groupingNum]
|
|
322
322
|
}).format(format)(price);
|
|
323
323
|
};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
|
-
import { storiesOf } from '@storybook/react';
|
|
4
|
-
import { action } from '@storybook/addon-actions';
|
|
5
|
-
|
|
6
|
-
import VehicleTitle from './VehicleTitle';
|
|
7
|
-
|
|
8
|
-
const container = {
|
|
9
|
-
maxWidth: 200
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const titleProps = {
|
|
13
|
-
manufacturer: 'BMW',
|
|
14
|
-
model: '320',
|
|
15
|
-
subModel: 'Touring Edition Fleet +PDC +Navi',
|
|
16
|
-
option: '135 (184) KW (PS)',
|
|
17
|
-
|
|
18
|
-
isSponsored: false,
|
|
19
|
-
onClick: action('click')
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
storiesOf('Vehicle title', module)
|
|
23
|
-
.add('default', () => (
|
|
24
|
-
<div style={container}>
|
|
25
|
-
<VehicleTitle {...titleProps} />
|
|
26
|
-
</div>
|
|
27
|
-
));
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
.text
|
|
2
|
-
margin-right: 5px
|
|
3
|
-
white-space: nowrap
|
|
4
|
-
|
|
5
|
-
.title
|
|
6
|
-
width: 100%
|
|
7
|
-
overflow: hidden
|
|
8
|
-
text-overflow: ellipsis
|
|
9
|
-
font-size: 14px
|
|
10
|
-
cursor: pointer
|
|
11
|
-
line-height: 20px
|
|
12
|
-
font-family: 'Arial Standard', 'Arial'
|
|
13
|
-
font-weight: 400
|
|
14
|
-
font-style: normal
|
|
15
|
-
|
|
16
|
-
.mainTitle, .optionTitle
|
|
17
|
-
display: flex
|
|
18
|
-
flex-direction: row
|
|
19
|
-
align-items: center
|
|
20
|
-
flex-wrap: nowrap
|
|
21
|
-
|
|
22
|
-
.brand
|
|
23
|
-
font-weight: 700
|
|
24
|
-
text-transform: capitalize
|
|
25
|
-
|
|
26
|
-
.sponsored
|
|
27
|
-
font-family: 'Arial Fett', 'Arial Standard', 'Arial'
|
|
28
|
-
font-weight: 700
|
|
29
|
-
font-style: normal
|
|
30
|
-
font-size: 10px
|
|
31
|
-
border 1px solid #CCC
|
|
32
|
-
padding: 2px 5px
|
|
33
|
-
box-sizing: border-box
|
|
34
|
-
border-radius 3px
|
|
35
|
-
line-height: 12px
|
|
36
|
-
text-transform: uppercase
|
|
37
|
-
|
|
38
|
-
.model
|
|
39
|
-
text-transform: capitalize
|
|
40
|
-
|
|
41
|
-
.subModel
|
|
42
|
-
font-weight normal
|
|
43
|
-
overflow: hidden
|
|
44
|
-
text-overflow: ellipsis
|
|
45
|
-
|
|
46
|
-
.option
|
|
47
|
-
margin-right: 5px
|
|
48
|
-
line-height: 20px
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import classnames from 'classnames';
|
|
3
|
-
|
|
4
|
-
import styles from './VehicleTitle.styl';
|
|
5
|
-
|
|
6
|
-
interface IVehicleTitleProps {
|
|
7
|
-
manufacturer?: string;
|
|
8
|
-
model?: string;
|
|
9
|
-
subModel?: string;
|
|
10
|
-
option?: string;
|
|
11
|
-
|
|
12
|
-
isSponsored?: boolean;
|
|
13
|
-
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const VehicleTitle: React.FC<IVehicleTitleProps> = (props) => {
|
|
17
|
-
const { manufacturer, model, subModel, option, isSponsored, onClick } = props;
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<div className={styles.title} onClick={onClick}>
|
|
21
|
-
<div className={styles.mainTitle}>
|
|
22
|
-
<span className={classnames(styles.text, styles.brand)}>{manufacturer}</span>
|
|
23
|
-
<span className={classnames(styles.text, styles.model)}>{model}</span>
|
|
24
|
-
<span className={classnames(styles.text, styles.subModel)}>{subModel}</span>
|
|
25
|
-
</div>
|
|
26
|
-
<div className={styles.optionTitle}>
|
|
27
|
-
<span className={styles.option}>{option}</span>
|
|
28
|
-
{isSponsored && <span className={styles.sponsored}>Sponsored</span>}
|
|
29
|
-
</div>
|
|
30
|
-
</div>
|
|
31
|
-
);
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
VehicleTitle.defaultProps = {
|
|
35
|
-
manufacturer: '',
|
|
36
|
-
model: '',
|
|
37
|
-
subModel: '',
|
|
38
|
-
option: '',
|
|
39
|
-
|
|
40
|
-
isSponsored: false,
|
|
41
|
-
onClick: () => { }
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export default VehicleTitle;
|