@bytebrand/fe-ui-core 4.2.66 → 4.2.68
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/UserDasboardPage/sections/OrderStatusSection/OrderStatusCar.test.tsx +4 -4
- package/__tests__/components/UserDasboardPage/sections/RequestedCarsSection/RequestedCarsSection.test.tsx +117 -0
- package/__tests__/components/_common/MaterialAutocomplete/MaterialAutocomplete.test.tsx +49 -0
- package/package.json +1 -1
- package/setupTests.js +8 -1
- package/source/components/UserDashboardPage/sections/OrderStatusSection/OrderStatusCar.tsx +2 -0
- package/source/components/UserDashboardPage/sections/RequestedCarsSection/RequestedCarsSection.story.js +97 -0
- package/source/components/UserDashboardPage/sections/RequestedCarsSection/RequestedCarsSection.styl +147 -0
- package/source/components/UserDashboardPage/sections/RequestedCarsSection/RequestedCarsSection.tsx +131 -0
- package/source/components/UserDashboardPage/sections/RequestedCarsSection/SupportSection.styl +47 -0
- package/source/components/UserDashboardPage/sections/RequestedCarsSection/SupportSection.tsx +32 -0
- package/source/components/_common/MaterialAutocomplete/MaterialAutocomplete.styled.tsx +8 -8
- package/source/components/_common/MaterialAutocomplete/MaterialAutocomplete.tsx +8 -12
- package/source/components/_common/Modal/modals/PreviewCookieModal/PreviewCookieModal.tsx +1 -1
- package/source/components/_common/OfferRequestButtonWrapper/OfferRequestButtonWrapper.tsx +1 -0
- package/source/framework/utils/CommonUtils.ts +1 -0
- package/tsconfig.json +3 -1
- package/tslint.json +0 -1
package/__tests__/components/UserDasboardPage/sections/OrderStatusSection/OrderStatusCar.test.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { render, fireEvent } from '@testing-library/react'
|
|
2
|
+
import { render, fireEvent } from '@testing-library/react';
|
|
3
3
|
import OrderStatusCar from '../../../../../source/components/UserDashboardPage/sections/OrderStatusSection/OrderStatusCar';
|
|
4
4
|
|
|
5
5
|
const t = (phrase: string | string[], options: object) => {
|
|
@@ -8,7 +8,7 @@ const t = (phrase: string | string[], options: object) => {
|
|
|
8
8
|
return `${phrase} ${value}`;
|
|
9
9
|
}
|
|
10
10
|
return phrase;
|
|
11
|
-
}
|
|
11
|
+
};
|
|
12
12
|
|
|
13
13
|
const mockProps = {
|
|
14
14
|
t,
|
|
@@ -25,7 +25,7 @@ const mockProps = {
|
|
|
25
25
|
registration: false,
|
|
26
26
|
currentSalesPrice: 30000,
|
|
27
27
|
onClick: jest.fn()
|
|
28
|
-
}
|
|
28
|
+
};
|
|
29
29
|
|
|
30
30
|
describe('OrderStatusCar', () => {
|
|
31
31
|
it('renders OrderStatusCar component without error', () => {
|
|
@@ -45,7 +45,7 @@ describe('OrderStatusCar', () => {
|
|
|
45
45
|
|
|
46
46
|
it('should display correct car image', () => {
|
|
47
47
|
const { getByTestId } = render(<OrderStatusCar {...mockProps}/>);
|
|
48
|
-
const carImage = getByTestId('car-image')
|
|
48
|
+
const carImage = getByTestId('car-image');
|
|
49
49
|
expect(carImage).toHaveAttribute('src', mockProps.imageUrl);
|
|
50
50
|
});
|
|
51
51
|
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { fireEvent, getByTestId, render, screen } from '@testing-library/react';
|
|
2
|
+
// import userEvent from '@testing-library/user-event';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import RequestedCarsSection from '../../../../../source/components/UserDashboardPage/sections/RequestedCarsSection/RequestedCarsSection';
|
|
5
|
+
|
|
6
|
+
const t = (phrase: string | string[], options: object) => {
|
|
7
|
+
if (options) {
|
|
8
|
+
const value = Object.values(options).map((option) => option);
|
|
9
|
+
return `${phrase} ${value}`;
|
|
10
|
+
}
|
|
11
|
+
return phrase;
|
|
12
|
+
};
|
|
13
|
+
interface IWithRouter {
|
|
14
|
+
children?: JSX.Element;
|
|
15
|
+
link?: string;
|
|
16
|
+
}
|
|
17
|
+
const LinkRouter = ({ children, link } :IWithRouter) => {
|
|
18
|
+
return (
|
|
19
|
+
<a href={link} >
|
|
20
|
+
{ children }
|
|
21
|
+
</a>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const requestedCars = [
|
|
26
|
+
{
|
|
27
|
+
car: {
|
|
28
|
+
_id: '28121b1a-398c-4e9c-9097-51be545817c5',
|
|
29
|
+
mainData: {
|
|
30
|
+
make: 'Volvo',
|
|
31
|
+
model: 'CX95',
|
|
32
|
+
subModel: 'Test123'
|
|
33
|
+
},
|
|
34
|
+
metaData: {
|
|
35
|
+
mainImageId: 'RQ_mHNek5hIk'
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
buyingType: 'leasing',
|
|
39
|
+
selfPickup: true,
|
|
40
|
+
paybackPeriod: 24,
|
|
41
|
+
monthlyInstallment: 2000,
|
|
42
|
+
request: '010203555',
|
|
43
|
+
currentSalesPrice: 50000
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
car: {
|
|
47
|
+
_id: '1234567890',
|
|
48
|
+
mainData: {
|
|
49
|
+
make: 'Volvo',
|
|
50
|
+
model: 'CX90',
|
|
51
|
+
subModel: 'Test155'
|
|
52
|
+
},
|
|
53
|
+
metaData: {
|
|
54
|
+
mainImageId: 'main image id'
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
buyingType: 'financing',
|
|
58
|
+
selfPickup: false,
|
|
59
|
+
paybackPeriod: 24,
|
|
60
|
+
monthlyInstallment: 1000,
|
|
61
|
+
request: '010203123',
|
|
62
|
+
currentSalesPrice: 20000
|
|
63
|
+
}
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
const mockProps = {
|
|
67
|
+
t,
|
|
68
|
+
requestedCars,
|
|
69
|
+
getSupportedImageFormat: jest.fn(),
|
|
70
|
+
redirectToCar: jest.fn(),
|
|
71
|
+
initHotjar: jest.fn(),
|
|
72
|
+
// tslint:disable-next-line:object-shorthand-properties-first
|
|
73
|
+
LinkRouter
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
describe('RequestedCarsSection', () => {
|
|
77
|
+
it('renders correctly', () => {
|
|
78
|
+
const { container } = render(<RequestedCarsSection {...mockProps} />);
|
|
79
|
+
expect(container).toBeInTheDocument();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should display correct car data', () => {
|
|
83
|
+
const { container } = render(<RequestedCarsSection {...mockProps} />);
|
|
84
|
+
const requestedCars = mockProps.requestedCars;
|
|
85
|
+
requestedCars.forEach((element) => {
|
|
86
|
+
expect(container).toHaveTextContent(element.buyingType);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
it('should display correct car make and model', () => {
|
|
90
|
+
const { getByText } = render(<RequestedCarsSection {...mockProps} />);
|
|
91
|
+
const requestedCars = mockProps.requestedCars;
|
|
92
|
+
requestedCars.forEach((element) => {
|
|
93
|
+
expect(getByText(`${element.car.mainData.make} ${element.car.mainData.model}`)).toBeInTheDocument();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
it('should display correct car subModel', () => {
|
|
97
|
+
const { getByText } = render(<RequestedCarsSection {...mockProps} />);
|
|
98
|
+
const requestedCars = mockProps.requestedCars;
|
|
99
|
+
requestedCars.forEach((element) => {
|
|
100
|
+
expect(getByText(`${element.car.mainData.subModel}`)).toBeInTheDocument();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
it('should display correct car request number', () => {
|
|
104
|
+
const { getByText } = render(<RequestedCarsSection {...mockProps} />);
|
|
105
|
+
const requestedCars = mockProps.requestedCars;
|
|
106
|
+
requestedCars.forEach((element) => {
|
|
107
|
+
expect(getByText(`${element.request}`)).toBeInTheDocument();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
it('calls redirectToCar with car ID on button click', () => {
|
|
111
|
+
const { getByTestId } = render(<RequestedCarsSection {...mockProps} />);
|
|
112
|
+
const carWrapper = getByTestId(mockProps.requestedCars[0].car._id);
|
|
113
|
+
fireEvent.click(carWrapper);
|
|
114
|
+
expect(mockProps.redirectToCar).toHaveBeenCalledTimes(1);
|
|
115
|
+
expect(mockProps.redirectToCar).toHaveBeenCalledWith(mockProps.requestedCars[0].car._id); // assert the correct car ID
|
|
116
|
+
});
|
|
117
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render, fireEvent } from "@testing-library/react";
|
|
3
|
+
import MaterialAutocomplete from "../../../../source/components/_common/MaterialAutocomplete/MaterialAutocomplete";
|
|
4
|
+
let value: any = null;
|
|
5
|
+
const setValue = jest.fn();
|
|
6
|
+
const materialAutocompleteProps = {
|
|
7
|
+
onChange: setValue,
|
|
8
|
+
label: 'test label',
|
|
9
|
+
value: value,
|
|
10
|
+
error: false,
|
|
11
|
+
required: true,
|
|
12
|
+
items: [
|
|
13
|
+
{ value: '+49', label: '+49', icon: 'de' },
|
|
14
|
+
{ value: '+43', label: '+43', icon: 'at' }
|
|
15
|
+
],
|
|
16
|
+
listWithImage: 'true'
|
|
17
|
+
}
|
|
18
|
+
describe('MaterialAutocomplete', () => {
|
|
19
|
+
it('renders component without errors', () => {
|
|
20
|
+
const { container, getByRole, getByText } = render(<MaterialAutocomplete {...materialAutocompleteProps} />)
|
|
21
|
+
expect(container).toBeInTheDocument();
|
|
22
|
+
const input = getByRole('combobox');
|
|
23
|
+
const button = getByRole('button');
|
|
24
|
+
fireEvent.click(button);
|
|
25
|
+
const listItem = getByText('+43');
|
|
26
|
+
fireEvent.click(listItem);
|
|
27
|
+
expect(input).toHaveAttribute('value', '+43');
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('renders component, open list and choose any option from list', () => {
|
|
31
|
+
const { getByRole, getByText } = render(<MaterialAutocomplete {...materialAutocompleteProps} />)
|
|
32
|
+
const input = getByRole('combobox');
|
|
33
|
+
const button = getByRole('button');
|
|
34
|
+
fireEvent.click(button);
|
|
35
|
+
const listItem = getByText('+43');
|
|
36
|
+
fireEvent.click(listItem);
|
|
37
|
+
expect(input).toHaveAttribute('value', '+43');
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('renders component, open list and check if we have icons in list, when "listWithImage: true"', () => {
|
|
41
|
+
const { getByRole, container } = render(<MaterialAutocomplete {...materialAutocompleteProps} />)
|
|
42
|
+
const button = getByRole('button');
|
|
43
|
+
fireEvent.click(button);
|
|
44
|
+
const iconDe = container.querySelector('[id="flag-icons-de"]');
|
|
45
|
+
const iconAt = container.querySelector('[id="flag-icons-at"]');
|
|
46
|
+
expect(iconDe).toBeInTheDocument();
|
|
47
|
+
expect(iconAt).toBeInTheDocument();
|
|
48
|
+
})
|
|
49
|
+
})
|
package/package.json
CHANGED
package/setupTests.js
CHANGED
|
@@ -4,10 +4,12 @@ import OrderStatusCard from './OrderStatusCard';
|
|
|
4
4
|
import Image from '../../../_common/Image/Image';
|
|
5
5
|
import { getFormattedPrice } from '../../../../framework/utils/CommonUtils';
|
|
6
6
|
|
|
7
|
+
// tslint:disable-next-line:interface-name
|
|
7
8
|
interface TFunction {
|
|
8
9
|
<T = string>(key: string, options?: object): T;
|
|
9
10
|
<T = string>(keys: string[], options?: object): T;
|
|
10
11
|
}
|
|
12
|
+
// tslint:disable-next-line:interface-name
|
|
11
13
|
interface IOrderStatusCar {
|
|
12
14
|
make?: string;
|
|
13
15
|
model?: string;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { storiesOf } from '@storybook/react';
|
|
3
|
+
|
|
4
|
+
import RequestedCarsSection from './RequestedCarsSection';
|
|
5
|
+
|
|
6
|
+
const requestedCars = [
|
|
7
|
+
{
|
|
8
|
+
car: {
|
|
9
|
+
_id: '1234567892',
|
|
10
|
+
mainData: {
|
|
11
|
+
make: 'Volvo',
|
|
12
|
+
model: 'CX90',
|
|
13
|
+
subModel: 'sub model',
|
|
14
|
+
},
|
|
15
|
+
metaData: {
|
|
16
|
+
mainImageId: 'main image id'
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
buyingType: 'leasing',
|
|
20
|
+
selfPickup: true,
|
|
21
|
+
paybackPeriod: 24,
|
|
22
|
+
monthlyInstallment: 1000,
|
|
23
|
+
status: 'selector_status_preparation',
|
|
24
|
+
request: '010203',
|
|
25
|
+
registration: true,
|
|
26
|
+
currentSalesPrice: 20000
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
car: {
|
|
30
|
+
_id: '1234567890',
|
|
31
|
+
mainData: {
|
|
32
|
+
make: 'Volvo',
|
|
33
|
+
model: 'CX90',
|
|
34
|
+
subModel: 'sub model',
|
|
35
|
+
},
|
|
36
|
+
metaData: {
|
|
37
|
+
mainImageId: 'main image id'
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
buyingType: 'financing',
|
|
41
|
+
selfPickup: false,
|
|
42
|
+
paybackPeriod: 24,
|
|
43
|
+
monthlyInstallment: 1000,
|
|
44
|
+
status: 'selector_status_registration',
|
|
45
|
+
request: '010203',
|
|
46
|
+
registration: false,
|
|
47
|
+
currentSalesPrice: 20000
|
|
48
|
+
}
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const translates = {
|
|
52
|
+
'DealerDashboardPage:buyingType.buy': 'buy',
|
|
53
|
+
'DealerDashboardPage:buyingType.leasing': 'leasing',
|
|
54
|
+
'DealerDashboardPage:buyingType.financing': 'financing',
|
|
55
|
+
'DealerDashboardPage:paybackPeriod': 'payback period',
|
|
56
|
+
'DealerDashboardPage:currentSalesPrice': 'current sales price',
|
|
57
|
+
'DealerDashboardPage:monthlyInstallment': 'monthly installment',
|
|
58
|
+
'DealerDashboardPage:requestedCars.requestedCarsTitle':'Requested cars',
|
|
59
|
+
'DealerDashboardPage:requestedCars.toOffer': 'To offer',
|
|
60
|
+
'DealerDashboardPage:requestedCars.customerService':'Support',
|
|
61
|
+
'DealerDashboardPage:requestedCars.customerServiceText':'Do you have questions about your desired vehicle or about car financing, delivery or registration? Please use the contact form and let us know your question. We will get back to you as soon as possible. Thank you very much.',
|
|
62
|
+
'DealerDashboardPage:requestedCars.contact':'Contact'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const t = (phrase, options) => {
|
|
66
|
+
if (options) {
|
|
67
|
+
console.log('qqqq options ====', options);
|
|
68
|
+
const value = Object.values(options).map((option) => option)
|
|
69
|
+
return `${translates[phrase]} ${value}`;
|
|
70
|
+
}
|
|
71
|
+
return translates[phrase];
|
|
72
|
+
}
|
|
73
|
+
const LinkRouter = ({ children, link }) => {
|
|
74
|
+
return (
|
|
75
|
+
<a href={link} >
|
|
76
|
+
{ children }
|
|
77
|
+
</a>
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
const initHotjar = () => {
|
|
81
|
+
console.log('Hotjar works')
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const props = {
|
|
85
|
+
t,
|
|
86
|
+
redirectToCar: (id) => console.log(`redirect to car with id: ${id}`),
|
|
87
|
+
getSupportedImageFormat: (id, mainImageId, size) => 'https://images.autode-dev.de/carimage/28121b1a-398c-4e9c-9097-51be545817c5/RQ_mHNek5hIk/small-cached.webp',
|
|
88
|
+
requestedCars,
|
|
89
|
+
LinkRouter,
|
|
90
|
+
initHotjar
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
storiesOf('userDashboard', module)
|
|
95
|
+
.add('RequestedCarsSection', () => (
|
|
96
|
+
<RequestedCarsSection {...props} />
|
|
97
|
+
));
|
package/source/components/UserDashboardPage/sections/RequestedCarsSection/RequestedCarsSection.styl
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
@import '../../../../theme/mixins.styl'
|
|
2
|
+
|
|
3
|
+
.requestedCarsSectionWrapper
|
|
4
|
+
border-right: 1px solid rgba(76, 78, 100, 0.12)
|
|
5
|
+
width: 219px!important
|
|
6
|
+
padding: 28px 10px 21px
|
|
7
|
+
cursor: pointer
|
|
8
|
+
display: flex!important;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
height: 305px;
|
|
11
|
+
justify-content: space-between;
|
|
12
|
+
+media-tablet-landscape-down()
|
|
13
|
+
width: 100% !important;
|
|
14
|
+
padding: 28px 10px 21px;
|
|
15
|
+
|
|
16
|
+
.carsContainer
|
|
17
|
+
display: flex;
|
|
18
|
+
+media-tablet-landscape-down()
|
|
19
|
+
width: 93vw;
|
|
20
|
+
overflow-x: scroll;
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
.model
|
|
24
|
+
font-style: normal;
|
|
25
|
+
font-weight: 500;
|
|
26
|
+
font-size: 20px;
|
|
27
|
+
line-height: 24px;
|
|
28
|
+
text-align: center;
|
|
29
|
+
letter-spacing: 0.15px;
|
|
30
|
+
color: rgba(76, 78, 100, 0.87);
|
|
31
|
+
max-height: 48px
|
|
32
|
+
display: flex
|
|
33
|
+
justify-content: center
|
|
34
|
+
-webkit-line-clamp: 1;
|
|
35
|
+
display: -webkit-box;
|
|
36
|
+
-webkit-box-orient: vertical;
|
|
37
|
+
overflow: hidden;
|
|
38
|
+
height: 26px;
|
|
39
|
+
|
|
40
|
+
.subModel
|
|
41
|
+
font-style: normal;
|
|
42
|
+
font-weight: 500;
|
|
43
|
+
font-size: 12px;
|
|
44
|
+
line-height: 16px;
|
|
45
|
+
text-align: center;
|
|
46
|
+
letter-spacing: 0.15px;
|
|
47
|
+
color: rgba(76, 78, 100, 0.87);
|
|
48
|
+
width: 100%;
|
|
49
|
+
-webkit-line-clamp: 1;
|
|
50
|
+
display: -webkit-box;
|
|
51
|
+
-webkit-box-orient: vertical;
|
|
52
|
+
overflow: hidden;
|
|
53
|
+
height: 16px;
|
|
54
|
+
|
|
55
|
+
.payment
|
|
56
|
+
height: 20px;
|
|
57
|
+
font-style: normal;
|
|
58
|
+
font-weight: 400;
|
|
59
|
+
font-size: 12px;
|
|
60
|
+
line-height: 20px;
|
|
61
|
+
text-align: center;
|
|
62
|
+
letter-spacing: 0.15px;
|
|
63
|
+
color: rgba(76, 78, 100, 0.87);
|
|
64
|
+
+media-tablet-landscape-down()
|
|
65
|
+
height: 40px;
|
|
66
|
+
|
|
67
|
+
.req
|
|
68
|
+
font-style: normal;
|
|
69
|
+
font-weight: 400;
|
|
70
|
+
font-size: 12px;
|
|
71
|
+
line-height: 16px;
|
|
72
|
+
text-align: center;
|
|
73
|
+
letter-spacing: 0.4px;
|
|
74
|
+
color: rgba(76, 78, 100, 0.87);
|
|
75
|
+
|
|
76
|
+
.offerButton
|
|
77
|
+
display: flex;
|
|
78
|
+
justify-content: center;
|
|
79
|
+
margin-top: 8px;
|
|
80
|
+
[class*='MuiButtonBase-root']
|
|
81
|
+
width: 187px
|
|
82
|
+
|
|
83
|
+
.container
|
|
84
|
+
display: flex
|
|
85
|
+
flex-direction: row
|
|
86
|
+
|
|
87
|
+
.defaultLayout
|
|
88
|
+
display: flex
|
|
89
|
+
flex-direction: row
|
|
90
|
+
justify-content: space-around
|
|
91
|
+
+media-tablet-landscape-down()
|
|
92
|
+
width: 100% !important;
|
|
93
|
+
|
|
94
|
+
.image
|
|
95
|
+
max-height: 164px;
|
|
96
|
+
overflow: hidden;
|
|
97
|
+
|
|
98
|
+
.sliderContainer
|
|
99
|
+
width: 720px;
|
|
100
|
+
+media-tablet-landscape-down()
|
|
101
|
+
width: 90vw;
|
|
102
|
+
:global
|
|
103
|
+
.slick-slider
|
|
104
|
+
overflow: hidden
|
|
105
|
+
+media-tablet-landscape-down()
|
|
106
|
+
padding 0;
|
|
107
|
+
.slick-arrow,
|
|
108
|
+
.slick-arrow:focus,
|
|
109
|
+
.slick-arrow:hover
|
|
110
|
+
display: inline-block
|
|
111
|
+
width: 25px
|
|
112
|
+
height: 50px
|
|
113
|
+
z-index: 1
|
|
114
|
+
transition: all 0.2s ease-in-out
|
|
115
|
+
.slick-arrow::before
|
|
116
|
+
display: none
|
|
117
|
+
.click-arrow::before:hover
|
|
118
|
+
background-color: black
|
|
119
|
+
|
|
120
|
+
.slick-arrow::after
|
|
121
|
+
position: absolute
|
|
122
|
+
content: ''
|
|
123
|
+
display: inline-block
|
|
124
|
+
width: inherit
|
|
125
|
+
height: inherit
|
|
126
|
+
top: 50%
|
|
127
|
+
transform: translateY(-50%)
|
|
128
|
+
transform-origin: top center
|
|
129
|
+
background-image: url('../../../../../media/images/slider-arrow-new.svg')
|
|
130
|
+
background-size: cover
|
|
131
|
+
|
|
132
|
+
.slick-next::after
|
|
133
|
+
left: 10px
|
|
134
|
+
|
|
135
|
+
.slick-prev::after
|
|
136
|
+
transform: rotate(180deg) translateY(-50%)
|
|
137
|
+
right: 0
|
|
138
|
+
|
|
139
|
+
.slick-prev
|
|
140
|
+
left: -5px!important
|
|
141
|
+
top:50%!important
|
|
142
|
+
|
|
143
|
+
.slick-next
|
|
144
|
+
right: -5px!important
|
|
145
|
+
top:50%!important
|
|
146
|
+
|
|
147
|
+
|
package/source/components/UserDashboardPage/sections/RequestedCarsSection/RequestedCarsSection.tsx
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import styles from './RequestedCarsSection.styl';
|
|
3
|
+
|
|
4
|
+
import Slider from 'react-slick';
|
|
5
|
+
import DashboardSection from '../../../containers/DasboardSection/DashboardSection';
|
|
6
|
+
import SupportSection from './SupportSection';
|
|
7
|
+
import i18next from 'i18next';
|
|
8
|
+
import { translate } from 'react-i18next';
|
|
9
|
+
import Image from '../../../_common/Image/Image';
|
|
10
|
+
import { Hidden, Visible } from 'react-grid-system';
|
|
11
|
+
import { getFormattedPrice } from '../../../../framework/utils/CommonUtils';
|
|
12
|
+
// import { history } from '../../../../../AppRouter';
|
|
13
|
+
import Button from '../../../_common/Button/Button';
|
|
14
|
+
|
|
15
|
+
interface IWithRouter {
|
|
16
|
+
children?: JSX.Element;
|
|
17
|
+
}
|
|
18
|
+
// tslint:disable-next-line:interface-name
|
|
19
|
+
interface TFunction {
|
|
20
|
+
<T = string>(key: string, options?: object): T;
|
|
21
|
+
<T = string>(keys: string[], options?: object): T;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface IRequestedCarsSection {
|
|
25
|
+
redirectToCar: (id: string) => void;
|
|
26
|
+
getSupportedImageFormat: (id: string, mainImageId: string, size: 'small' | 'medium' | 'large') => string;
|
|
27
|
+
requestedCars: any[];
|
|
28
|
+
t: TFunction;
|
|
29
|
+
LinkRouter:any;
|
|
30
|
+
initHotjar:() => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const RequestedCarsSection = ({ t, getSupportedImageFormat, requestedCars, redirectToCar, LinkRouter, initHotjar }: IRequestedCarsSection) => {
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
initHotjar();
|
|
36
|
+
}, []);
|
|
37
|
+
const onDetailsClick = (event: React.MouseEvent, carId: string) => {
|
|
38
|
+
redirectToCar(carId);
|
|
39
|
+
event.preventDefault();
|
|
40
|
+
event.stopPropagation();
|
|
41
|
+
// history.push(`/search/vehicle/${carId}`);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const renderItems = () => {
|
|
45
|
+
if (requestedCars && requestedCars.length > 0) {
|
|
46
|
+
const carsCard = requestedCars.map((car) => {
|
|
47
|
+
if (car && car.car) {
|
|
48
|
+
const { car: { mainData: { make, model, subModel } } } = car;
|
|
49
|
+
const { car: { _id, metaData: { mainImageId } } } = car;
|
|
50
|
+
const { buyingType, paybackPeriod, monthlyInstallment, request, currentSalesPrice } = car;
|
|
51
|
+
const imageUrl = getSupportedImageFormat(_id, mainImageId, 'small');
|
|
52
|
+
return (
|
|
53
|
+
<div key={_id} className={styles.requestedCarsSectionWrapper} data-testid={_id} onClick={(e: React.MouseEvent<HTMLDivElement>) => onDetailsClick(e, _id)}>
|
|
54
|
+
<div className={styles.model}>{make} {model}</div>
|
|
55
|
+
<div className={styles.subModel}>{subModel}</div>
|
|
56
|
+
<Image className={styles.image} width='100%' ratioW={4} ratioH={3} data-testid='car-image' src={imageUrl} />
|
|
57
|
+
<div className={styles.req}>{request}</div>
|
|
58
|
+
{
|
|
59
|
+
(buyingType !== 'buy')
|
|
60
|
+
? (<div className={styles.payment}> {t(`DealerDashboardPage:buyingType.${buyingType}`)}, {t('DealerDashboardPage:paybackPeriod', { paybackPeriod })}, {t('DealerDashboardPage:monthlyInstallment', { monthlyInstallment: getFormattedPrice(monthlyInstallment, '$,.2f') })}</div>)
|
|
61
|
+
: (<div className={styles.payment}>
|
|
62
|
+
{t(`DealerDashboardPage:buyingType.${buyingType}`)}, {t('DealerDashboardPage:currentSalesPrice', { currentSalesPrice: getFormattedPrice(currentSalesPrice) })}
|
|
63
|
+
</div>)
|
|
64
|
+
}
|
|
65
|
+
<div></div>
|
|
66
|
+
<div className={styles.offerButton}>
|
|
67
|
+
<Button variant='outlined' size='small' onClick={(e: React.MouseEvent<HTMLButtonElement>) => { onDetailsClick(e, _id); }}>{t('DealerDashboardPage:requestedCars.toOffer')}</Button>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
return carsCard.reverse();
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const renderSlider = () => {
|
|
78
|
+
const sliderProps = {
|
|
79
|
+
className: 'react-slider-container',
|
|
80
|
+
slidesToShow: 3,
|
|
81
|
+
slidesToScroll: 1,
|
|
82
|
+
dots: false,
|
|
83
|
+
arrow: false,
|
|
84
|
+
rows: 1,
|
|
85
|
+
infinity: false
|
|
86
|
+
};
|
|
87
|
+
return (
|
|
88
|
+
<>
|
|
89
|
+
<Visible xs sm md>
|
|
90
|
+
<div className={styles.defaultLayout}>
|
|
91
|
+
{renderItems()}
|
|
92
|
+
</div>
|
|
93
|
+
</Visible>
|
|
94
|
+
<Hidden xs sm md>
|
|
95
|
+
{(requestedCars.length >= 3) ? (
|
|
96
|
+
<div className={styles.sliderContainer}>
|
|
97
|
+
<Slider {...sliderProps}>
|
|
98
|
+
{renderItems()}
|
|
99
|
+
</Slider>
|
|
100
|
+
</div>) :
|
|
101
|
+
(<div className={styles.defaultLayout}>
|
|
102
|
+
{renderItems()}
|
|
103
|
+
</div>)
|
|
104
|
+
}
|
|
105
|
+
</Hidden>
|
|
106
|
+
</>
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
const SupportSectionProps = {
|
|
110
|
+
t,
|
|
111
|
+
Link:LinkRouter
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<>
|
|
116
|
+
{requestedCars.length > 0 ? (
|
|
117
|
+
<DashboardSection title={t('DealerDashboardPage:requestedCars.requestedCarsTitle')}>
|
|
118
|
+
<div className={styles.carsContainer}>
|
|
119
|
+
<div className={styles.wrapper}>
|
|
120
|
+
{renderSlider()}
|
|
121
|
+
</div>
|
|
122
|
+
<SupportSection {...SupportSectionProps} />
|
|
123
|
+
</div>
|
|
124
|
+
</DashboardSection>
|
|
125
|
+
) : (
|
|
126
|
+
null
|
|
127
|
+
)}
|
|
128
|
+
</>
|
|
129
|
+
);
|
|
130
|
+
};
|
|
131
|
+
export default RequestedCarsSection;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
@import '../../../../theme/mixins.styl'
|
|
2
|
+
.supportSection
|
|
3
|
+
padding: 22px 20px
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
justify-content: space-between;
|
|
7
|
+
+media-tablet-landscape-down()
|
|
8
|
+
padding: 22px 10px;
|
|
9
|
+
|
|
10
|
+
.circle
|
|
11
|
+
width: 56px;
|
|
12
|
+
height: 56px;
|
|
13
|
+
background: linear-gradient(0deg, rgba(255, 255, 255, 0.88), rgba(255, 255, 255, 0.88)), #666CFF;
|
|
14
|
+
border-radius: 56px;
|
|
15
|
+
display: flex;
|
|
16
|
+
justify-content: center;
|
|
17
|
+
align-items: center;
|
|
18
|
+
margin: 0px auto;
|
|
19
|
+
.title
|
|
20
|
+
font-style: normal;
|
|
21
|
+
font-weight: 500;
|
|
22
|
+
font-size: 20px;
|
|
23
|
+
line-height: 32px;
|
|
24
|
+
text-align: center;
|
|
25
|
+
letter-spacing: 0.15px;
|
|
26
|
+
color: rgba(76, 78, 100, 0.87);
|
|
27
|
+
|
|
28
|
+
.text
|
|
29
|
+
font-style: normal;
|
|
30
|
+
font-weight: 400;
|
|
31
|
+
font-size: 14px;
|
|
32
|
+
line-height: 20px;
|
|
33
|
+
text-align: center;
|
|
34
|
+
letter-spacing: 0.15px;
|
|
35
|
+
color: rgba(76, 78, 100, 0.68);
|
|
36
|
+
+media-tablet-landscape-down()
|
|
37
|
+
width: 88vw
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
.contactButton
|
|
41
|
+
display: flex;
|
|
42
|
+
justify-content: center;
|
|
43
|
+
margin-top: 8px;
|
|
44
|
+
[class*='MuiButtonBase-root']
|
|
45
|
+
background-color: rgba(0, 184, 0, 1);
|
|
46
|
+
width: 111px;
|
|
47
|
+
height: 42px;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// import { Button, IconSVG } from '@bytebrand/fe-ui-core/common';
|
|
2
|
+
import i18next from 'i18next';
|
|
3
|
+
import IconSVG from '../../../_common/IconSVG/IconSVG';
|
|
4
|
+
import Button from '../../../_common/Button/Button';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import styles from './SupportSection.styl';
|
|
7
|
+
|
|
8
|
+
export interface IProps {
|
|
9
|
+
t: i18next.TFunction;
|
|
10
|
+
Link?: any;
|
|
11
|
+
}
|
|
12
|
+
const SupportSection: React.FunctionComponent<IProps> = ({ t , Link }) => {
|
|
13
|
+
return (
|
|
14
|
+
<div className={styles.supportSection}>
|
|
15
|
+
<div className={styles.circle}>
|
|
16
|
+
<IconSVG className={styles.icon} name='dashboardQuestionMark' customDimensions />
|
|
17
|
+
</div>
|
|
18
|
+
<div className={styles.title}>
|
|
19
|
+
{t('DealerDashboardPage:requestedCars.customerService')}
|
|
20
|
+
</div>
|
|
21
|
+
<div className={styles.text}>
|
|
22
|
+
{t('DealerDashboardPage:requestedCars.customerServiceText')}</div>
|
|
23
|
+
<div className={styles.contactButton}>
|
|
24
|
+
<Link link='/account/kontakt'>
|
|
25
|
+
<Button size='small' color='success' variant='contained'>{t('DealerDashboardPage:requestedCars.contact')}</Button>
|
|
26
|
+
</Link>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default SupportSection;
|
|
@@ -114,9 +114,9 @@ export const Theme = createTheme({
|
|
|
114
114
|
},
|
|
115
115
|
MuiInputLabel: {
|
|
116
116
|
styleOverrides: {
|
|
117
|
-
root: ({ ownerState }) => ({
|
|
117
|
+
root: ({ ownerState: { size } }: { ownerState: { size?: string } }) => ({
|
|
118
118
|
maxWidth: 'calc(100% - 28px)',
|
|
119
|
-
...(
|
|
119
|
+
...(size === 'custom' && {
|
|
120
120
|
marginTop: isMobileOnly ? '0px' :'-4px',
|
|
121
121
|
['&.MuiInputLabel-shrink, &.Mui-focused']: {
|
|
122
122
|
marginTop: 0
|
|
@@ -127,8 +127,8 @@ export const Theme = createTheme({
|
|
|
127
127
|
},
|
|
128
128
|
MuiOutlinedInput: {
|
|
129
129
|
styleOverrides: {
|
|
130
|
-
root: ({ ownerState }) => ({
|
|
131
|
-
...(
|
|
130
|
+
root: ({ ownerState: { size, name } }: { ownerState: { size?: string, name?: string } }) => ({
|
|
131
|
+
...(size === 'small' && {
|
|
132
132
|
paddingRight: '0 !important',
|
|
133
133
|
flexWrap: 'nowrap !important',
|
|
134
134
|
backgroundColor: '#fff',
|
|
@@ -150,11 +150,11 @@ export const Theme = createTheme({
|
|
|
150
150
|
opacity:'0.38'
|
|
151
151
|
},
|
|
152
152
|
['& .MuiAutocomplete-input']: {
|
|
153
|
-
...(
|
|
153
|
+
...(name === 'mobileSearch' && {
|
|
154
154
|
textAlign: isMobileOnly ? 'right !important' : 'left'
|
|
155
155
|
})
|
|
156
156
|
},
|
|
157
|
-
...(
|
|
157
|
+
...(size === 'custom' && {
|
|
158
158
|
height: isMobileOnly ? 56 : 48,
|
|
159
159
|
boxSizing: 'border-box',
|
|
160
160
|
backgroundColor: '#fff',
|
|
@@ -164,7 +164,7 @@ export const Theme = createTheme({
|
|
|
164
164
|
padding: '5.5px 4px 7.5px 6px !important'
|
|
165
165
|
}
|
|
166
166
|
}),
|
|
167
|
-
...(
|
|
167
|
+
...(name === 'mobileSearch' && {
|
|
168
168
|
color: isMobileOnly ? '#005ccb' : 'rgba(0, 0, 0, 0.87)',
|
|
169
169
|
backgroundColor: isMobileOnly ? 'transparent' : '#fff',
|
|
170
170
|
paddingRight: isMobileOnly ? '33px !important' : '42px !important',
|
|
@@ -181,7 +181,7 @@ export const Theme = createTheme({
|
|
|
181
181
|
borderWidth: isMobileOnly ? 0 : 1
|
|
182
182
|
}
|
|
183
183
|
})
|
|
184
|
-
})
|
|
184
|
+
} as any)
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
187
|
}
|
|
@@ -11,6 +11,7 @@ import Autocomplete, { createFilterOptions } from '@mui/material/Autocomplete';
|
|
|
11
11
|
import { Theme, ArrowSelect, CheckboxLabel } from './MaterialAutocomplete.styled';
|
|
12
12
|
import isEqual from 'lodash/isEqual';
|
|
13
13
|
import IconSVG from '../IconSVG/IconSVG';
|
|
14
|
+
import { isMobileOnly } from 'react-device-detect';
|
|
14
15
|
|
|
15
16
|
export interface IItems {
|
|
16
17
|
value: string | number;
|
|
@@ -100,11 +101,8 @@ const MaterialAutocomplete: React.FC<IMaterialAutocompleteProps> = ({
|
|
|
100
101
|
multiple={multiple}
|
|
101
102
|
handleHomeEndKeys
|
|
102
103
|
value={value}
|
|
103
|
-
name={name}
|
|
104
|
-
error={error ? error.toString() : ''}
|
|
105
104
|
readOnly={readOnly}
|
|
106
|
-
|
|
107
|
-
onChange={(e, newValue, reason: string, details?: { option: any }) => {
|
|
105
|
+
onChange={(e, newValue, _: string, details?: { option: any }) => {
|
|
108
106
|
e.persist();
|
|
109
107
|
if (typeof newValue === 'string') {
|
|
110
108
|
onChange(newValue);
|
|
@@ -150,17 +148,16 @@ const MaterialAutocomplete: React.FC<IMaterialAutocompleteProps> = ({
|
|
|
150
148
|
}}
|
|
151
149
|
options={multiple ? items.map(option => option.label) : items}
|
|
152
150
|
ListboxProps={{
|
|
153
|
-
|
|
154
|
-
maxHeight:
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
151
|
+
style: {
|
|
152
|
+
maxHeight: isMobileOnly ?
|
|
153
|
+
MOBILE_ITEM_HEIGHT * MENU_ITEMS
|
|
154
|
+
: ITEM_HEIGHT * MENU_ITEMS
|
|
158
155
|
}
|
|
159
156
|
}}
|
|
160
157
|
forcePopupIcon
|
|
161
158
|
popupIcon={disableIcon ? '' : <ArrowSelect name='arrowSelect' customDimensions />}
|
|
162
159
|
isOptionEqualToValue={(option, value) => option.value === value}
|
|
163
|
-
renderOption={(props, option) => {
|
|
160
|
+
renderOption={(props: any, option) => {
|
|
164
161
|
if (multiple) {
|
|
165
162
|
return (
|
|
166
163
|
<li {...props}>
|
|
@@ -218,8 +215,7 @@ const MaterialAutocomplete: React.FC<IMaterialAutocompleteProps> = ({
|
|
|
218
215
|
InputProps={{ ...params.InputProps, readOnly }}
|
|
219
216
|
/>
|
|
220
217
|
)}
|
|
221
|
-
|
|
222
|
-
</Autocomplete>
|
|
218
|
+
/>
|
|
223
219
|
</ThemeProvider>
|
|
224
220
|
);
|
|
225
221
|
};
|
|
@@ -12,7 +12,7 @@ interface IPreviewCookieModal {
|
|
|
12
12
|
|
|
13
13
|
const PreviewCookieModal = ({ toggleModal, setModal }: IPreviewCookieModal) => {
|
|
14
14
|
const onAcceptAll = () => {
|
|
15
|
-
|
|
15
|
+
localStorage.setItem('cookieConfig', JSON.stringify({}));
|
|
16
16
|
updateCookieList();
|
|
17
17
|
toggleModal();
|
|
18
18
|
};
|
|
@@ -54,6 +54,7 @@ const OfferRequestBtnWrapper: React.FunctionComponent<IOfferRequestButtonWrapper
|
|
|
54
54
|
|
|
55
55
|
const getRequestButtonTitle = () => {
|
|
56
56
|
if (isSale) return t('sidebar.requestOfferSale');
|
|
57
|
+
// tslint:disable-next-line:no-else-after-return
|
|
57
58
|
else if (isAlternativeType) return t('sidebar.importRequest');
|
|
58
59
|
else if (hasCheckout) return t('vehicleProps:title.toCheckoutCar');
|
|
59
60
|
else if (isOfferRequested) return t('CheckoutPage:onlineCheckoutModal.redirectBtn');
|
|
@@ -15,6 +15,7 @@ import { SearchPage as SearchPageTranslate } from '../../locales/data';
|
|
|
15
15
|
const PRICE_DEFAULT = DROP_DOWN_GROUP[PRICE].defaultValue;
|
|
16
16
|
|
|
17
17
|
declare global {
|
|
18
|
+
// tslint:disable-next-line:interface-name
|
|
18
19
|
interface Window {
|
|
19
20
|
grantHotjarCookieConsent?: () => void;
|
|
20
21
|
grantCookieConsent?: (list: string[]) => void;
|
package/tsconfig.json
CHANGED
package/tslint.json
CHANGED
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
"no-console": [true, "warn", "error"],
|
|
21
21
|
"switch-default": true,
|
|
22
22
|
"no-empty-interface": true,
|
|
23
|
-
// "no-magic-numbers": [true, 0, -1, 1, 2, 10, 8, 16],
|
|
24
23
|
"no-parameter-reassignment": true,
|
|
25
24
|
"no-duplicate-switch-case": true,
|
|
26
25
|
"no-duplicate-imports": true,
|