@nyris/nyris-webapp 0.3.98 → 0.4.0
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/build/asset-manifest.json +7 -6
- package/build/index.html +1 -1
- package/build/js/settings.example.js +42 -0
- package/build/static/css/main.a7ff7a7f.css +4 -0
- package/build/static/css/main.a7ff7a7f.css.map +1 -0
- package/build/static/js/main.916c157c.js +3 -0
- package/build/static/js/{main.93fdbd6e.js.map → main.916c157c.js.map} +1 -1
- package/build/static/media/plate_info.6beaea36d35053be3a45.png +0 -0
- package/package.json +3 -3
- package/public/js/settings.example.js +42 -0
- package/src/common/assets/images/plate_info.png +0 -0
- package/src/components/Cart.tsx +41 -0
- package/src/components/ChatAssistant/ChatAssistant.test.tsx +213 -0
- package/src/components/ChatAssistant/ChatAssistant.tsx +18 -1
- package/src/components/ChatAssistant/MobileChatAssistant.tsx +18 -1
- package/src/components/ChatAssistant/OptionChip.tsx +23 -1
- package/src/components/ChatAssistant/useChatAssistantLogic.ts +35 -14
- package/src/components/GoBackButton.tsx +29 -2
- package/src/components/Header.tsx +3 -0
- package/src/components/HitsPerPage.tsx +55 -18
- package/src/components/ImagePreview.tsx +31 -1
- package/src/components/ImageUpload.tsx +12 -1
- package/src/components/Inquiry/InquiryModal.tsx +61 -26
- package/src/components/Pagination.tsx +33 -6
- package/src/components/PostFilter/PostFilter.tsx +8 -1
- package/src/components/PostFilter/PostFilterFindApi.test.tsx +249 -0
- package/src/components/PostFilter/PostFilterFindApi.tsx +128 -52
- package/src/components/PreFilter/PreFilter.tsx +10 -2
- package/src/components/Product/Product.tsx +60 -10
- package/src/components/Product/ProductDetailView.tsx +23 -1
- package/src/components/Product/ProductDetailViewModal.tsx +5 -1
- package/src/components/Product/ProductList.tsx +51 -18
- package/src/components/Rfq/RfqModal.tsx +3 -0
- package/src/components/ScopeSelect/ScopeSelectForm.tsx +514 -0
- package/src/components/ScopeSelect/ScopeSelectImageActions.tsx +62 -0
- package/src/components/ScopeSelect/ScopeSelectImagePreview.tsx +141 -0
- package/src/components/ScopeSelect/ScopeSelectInfo.tsx +68 -0
- package/src/components/ScopeSelect/ScopeSelectInput.tsx +123 -0
- package/src/components/ScopeSelect/ScopeSelectModal.tsx +310 -0
- package/src/components/ScopeSelect/identifyTypePlateImage.test.ts +129 -0
- package/src/components/ScopeSelect/identifyTypePlateImage.ts +83 -0
- package/src/components/ScopeSelect/parseScopeIdResponse.test.ts +103 -0
- package/src/components/ScopeSelect/parseScopeIdResponse.ts +72 -0
- package/src/components/ScopeSelect/scopeSelectHelpers.ts +54 -0
- package/src/components/SidePanel.tsx +33 -0
- package/src/components/TextSearch.tsx +77 -11
- package/src/components/Tooltip/TooltipComponent.tsx +12 -4
- package/src/components/TypePlateUpload.tsx +132 -0
- package/src/hooks/chatProductMetadata.test.ts +307 -0
- package/src/hooks/useBadResultsRecovery.ts +26 -49
- package/src/hooks/useEffectiveGroundingResults.ts +8 -6
- package/src/hooks/useGoodResultsChat.ts +28 -39
- package/src/hooks/useImageSearch.ts +290 -12
- package/src/hooks/useMachinePartsListingPagination.ts +95 -0
- package/src/hooks/useResultEvaluator.ts +63 -73
- package/src/index.css +1 -1
- package/src/layouts/AppLayout.tsx +43 -3
- package/src/pages/Home.tsx +60 -14
- package/src/pages/Result.tsx +194 -14
- package/src/services/Feedback.ts +225 -34
- package/src/services/feedbackSubtypes.ts +63 -0
- package/src/services/filter.ts +1 -1
- package/src/services/initialMachinePartsText.payload.test.ts +61 -0
- package/src/services/initialMachinePartsText.test.ts +33 -0
- package/src/services/initialMachinePartsText.ts +11 -0
- package/src/services/machinePartsPage.test.ts +74 -0
- package/src/services/machinePartsPage.ts +120 -0
- package/src/services/normalizeChatResponse.ts +26 -0
- package/src/services/scopeLookup.test.ts +67 -0
- package/src/services/scopeLookup.ts +39 -0
- package/src/services/visualSearch.listingOptions.test.ts +79 -0
- package/src/services/visualSearch.ts +71 -3
- package/src/services/vizo.ts +4 -3
- package/src/stores/chat/conversationStore.test.ts +133 -0
- package/src/stores/chat/conversationStore.ts +55 -14
- package/src/stores/machinePartsListing/machinePartsListingStore.ts +35 -0
- package/src/stores/request/filter/filter.initialState.ts +1 -0
- package/src/stores/request/filter/filter.slice.ts +2 -0
- package/src/stores/result/prodcuts/products.initialState.ts +1 -0
- package/src/stores/result/prodcuts/products.slice.ts +4 -0
- package/src/stores/scope/scopeStore.ts +67 -4
- package/src/stores/types.ts +19 -14
- package/src/translations.ts +213 -0
- package/src/types.ts +41 -0
- package/src/utils/chatProductMetadata.test.ts +130 -0
- package/src/utils/chatProductMetadata.ts +93 -0
- package/src/utils/compressImage.ts +3 -3
- package/src/utils/fetchProductImage.ts +1 -0
- package/src/utils/normalizeChatAssistantResponse.ts +227 -0
- package/src/utils/productCardImageUrl.test.ts +41 -0
- package/src/utils/productCardImageUrl.ts +49 -0
- package/src/utils/textSearchFilter.test.ts +41 -0
- package/src/utils/textSearchFilter.ts +13 -6
- package/build/static/css/main.81216c5d.css +0 -4
- package/build/static/css/main.81216c5d.css.map +0 -1
- package/build/static/js/main.93fdbd6e.js +0 -3
- /package/build/static/js/{main.93fdbd6e.js.LICENSE.txt → main.916c157c.js.LICENSE.txt} +0 -0
|
@@ -7,8 +7,11 @@ import useResultStore from 'stores/result/resultStore';
|
|
|
7
7
|
import useRequestStore from 'stores/request/requestStore';
|
|
8
8
|
import { twMerge } from 'tailwind-merge';
|
|
9
9
|
import useUiStore from 'stores/ui/uiStore';
|
|
10
|
+
import useScopeStore from 'stores/scope/scopeStore';
|
|
11
|
+
import { useImageSearch } from 'hooks/useImageSearch';
|
|
10
12
|
|
|
11
13
|
export const GoBackButton = ({ className }: { className?: string }) => {
|
|
14
|
+
const { singleImageSearch } = useImageSearch();
|
|
12
15
|
const firstSearchResults = useResultStore(state => state.firstSearchResults);
|
|
13
16
|
const setFindApiProducts = useResultStore(state => state.setFindApiProducts);
|
|
14
17
|
const setImageAnalysis = useResultStore(state => state.setImageAnalysis);
|
|
@@ -27,12 +30,35 @@ export const GoBackButton = ({ className }: { className?: string }) => {
|
|
|
27
30
|
const setAlgoliaFilter = useRequestStore(state => state.setAlgoliaFilter);
|
|
28
31
|
|
|
29
32
|
const isFindApiLoading = useUiStore(state => state.isFindApiLoading);
|
|
33
|
+
const scopeId = useScopeStore(state => state.scopeId);
|
|
34
|
+
const scopeItemKeyCount = useScopeStore(state => state.itemKeys.length);
|
|
35
|
+
const machineScopeActive =
|
|
36
|
+
Boolean(window.settings.scopes?.scopeID) &&
|
|
37
|
+
Boolean(scopeId) &&
|
|
38
|
+
scopeItemKeyCount > 0;
|
|
30
39
|
|
|
31
40
|
const { refine: clearPostFilters } = useClearRefinements();
|
|
32
41
|
|
|
33
42
|
const { t } = useTranslation();
|
|
34
43
|
|
|
35
44
|
const onGoBack = () => {
|
|
45
|
+
// The saved snapshot may predate the machine scope, so search the original
|
|
46
|
+
// image again; find() applies the active scope.
|
|
47
|
+
if (machineScopeActive && firstSearchImage) {
|
|
48
|
+
setPreFilter(firstSearchPreFilter);
|
|
49
|
+
resetRegions();
|
|
50
|
+
clearPostFilters();
|
|
51
|
+
void singleImageSearch({
|
|
52
|
+
image: firstSearchImage,
|
|
53
|
+
settings: window.settings,
|
|
54
|
+
showFeedback: true,
|
|
55
|
+
newSearch: false,
|
|
56
|
+
compress: false,
|
|
57
|
+
clearPostFilter: true,
|
|
58
|
+
});
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
36
62
|
setFindApiProducts(firstSearchResults);
|
|
37
63
|
if (firstSearchImage) {
|
|
38
64
|
setRequestImages([firstSearchImage]);
|
|
@@ -58,9 +84,10 @@ export const GoBackButton = ({ className }: { className?: string }) => {
|
|
|
58
84
|
};
|
|
59
85
|
|
|
60
86
|
if (
|
|
61
|
-
|
|
87
|
+
!firstSearchImage ||
|
|
62
88
|
requestImages[0] === firstSearchImage ||
|
|
63
|
-
isFindApiLoading
|
|
89
|
+
isFindApiLoading ||
|
|
90
|
+
(!machineScopeActive && firstSearchResults.length === 0)
|
|
64
91
|
)
|
|
65
92
|
return <></>;
|
|
66
93
|
|
|
@@ -12,6 +12,7 @@ import LogoutModal from './LogoutModal';
|
|
|
12
12
|
import useResultStore from 'stores/result/resultStore';
|
|
13
13
|
import Cart from './Cart';
|
|
14
14
|
import useUiStore from 'stores/ui/uiStore';
|
|
15
|
+
import useScopeStore from 'stores/scope/scopeStore';
|
|
15
16
|
|
|
16
17
|
function Header() {
|
|
17
18
|
const { theme, auth0 } = window.settings;
|
|
@@ -20,6 +21,7 @@ function Header() {
|
|
|
20
21
|
|
|
21
22
|
const reset = useRequestStore(state => state.reset);
|
|
22
23
|
const resetResultStore = useResultStore(state => state.reset);
|
|
24
|
+
const resetScope = useScopeStore(state => state.resetScope);
|
|
23
25
|
const setSpecifications = useRequestStore(state => state.setSpecifications);
|
|
24
26
|
const showSidePanel = useUiStore(state => state.showSidePanel);
|
|
25
27
|
const setShowSidePanel = useUiStore(state => state.setShowSidePanel);
|
|
@@ -84,6 +86,7 @@ function Header() {
|
|
|
84
86
|
onClick={() => {
|
|
85
87
|
reset();
|
|
86
88
|
resetResultStore();
|
|
89
|
+
resetScope();
|
|
87
90
|
setSpecifications(null);
|
|
88
91
|
}}
|
|
89
92
|
className="pl-6"
|
|
@@ -2,13 +2,31 @@ import { useTranslation } from 'react-i18next';
|
|
|
2
2
|
import { useHitsPerPage, UseHitsPerPageProps } from 'react-instantsearch';
|
|
3
3
|
import PoweredBy from './PoweredBy';
|
|
4
4
|
import { twMerge } from 'tailwind-merge';
|
|
5
|
+
import useMachinePartsListingStore from 'stores/machinePartsListing/machinePartsListingStore';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
interface Props extends UseHitsPerPageProps {
|
|
8
|
+
onFindApiPageSizeChange?: (pageSize: number) => void;
|
|
9
|
+
useFindApiPageSize?: boolean;
|
|
10
|
+
showPageSize?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const HitsPerPage = ({
|
|
14
|
+
onFindApiPageSizeChange,
|
|
15
|
+
useFindApiPageSize = false,
|
|
16
|
+
showPageSize = true,
|
|
17
|
+
...props
|
|
18
|
+
}: Props) => {
|
|
7
19
|
const { items, refine } = useHitsPerPage(props);
|
|
8
20
|
const { value: currentValue } =
|
|
9
21
|
items.find(({ isRefined }) => isRefined)! || {};
|
|
10
|
-
|
|
22
|
+
const pageSize = useMachinePartsListingStore(state => state.pageSize);
|
|
11
23
|
const { t } = useTranslation();
|
|
24
|
+
const isAlgoliaEnabled = window.settings.algolia.enabled;
|
|
25
|
+
const controlAlgoliaPageSize = isAlgoliaEnabled && !useFindApiPageSize;
|
|
26
|
+
const options = controlAlgoliaPageSize ? items : props.items || items;
|
|
27
|
+
const selectedValue = controlAlgoliaPageSize
|
|
28
|
+
? String(currentValue)
|
|
29
|
+
: String(pageSize);
|
|
12
30
|
|
|
13
31
|
return (
|
|
14
32
|
<>
|
|
@@ -18,25 +36,44 @@ export const HitsPerPage = (props: UseHitsPerPageProps) => {
|
|
|
18
36
|
'shadow-ds-1',
|
|
19
37
|
)}
|
|
20
38
|
>
|
|
21
|
-
|
|
22
|
-
<div className="
|
|
23
|
-
|
|
39
|
+
{showPageSize && (
|
|
40
|
+
<div className="self-stretch pl-4 pr-6 border-r border-solid border-[#DDDEE7] justify-center items-center gap-1 flex">
|
|
41
|
+
<div className="text-[#3B3E5F] text-[13px] font-normal font-['Source Sans 3'] leading-none tracking-tight">
|
|
42
|
+
{t('Items per page')}:
|
|
43
|
+
</div>
|
|
44
|
+
<select
|
|
45
|
+
className="bg-transparent text-[13px] outline-none"
|
|
46
|
+
onChange={event => {
|
|
47
|
+
const nextPageSize = Number(event.target.value);
|
|
48
|
+
if (controlAlgoliaPageSize) {
|
|
49
|
+
refine(nextPageSize);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
onFindApiPageSizeChange?.(nextPageSize);
|
|
53
|
+
}}
|
|
54
|
+
value={selectedValue}
|
|
55
|
+
>
|
|
56
|
+
{options.map(item => (
|
|
57
|
+
<option key={item.value} value={item.value}>
|
|
58
|
+
{item.label}
|
|
59
|
+
</option>
|
|
60
|
+
))}
|
|
61
|
+
</select>
|
|
24
62
|
</div>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
63
|
+
)}
|
|
64
|
+
{!isAlgoliaEnabled || !showPageSize ? (
|
|
65
|
+
<div
|
|
66
|
+
style={{
|
|
67
|
+
display: 'flex',
|
|
68
|
+
justifyContent: 'end',
|
|
69
|
+
marginLeft: 'auto',
|
|
29
70
|
}}
|
|
30
|
-
value={String(currentValue)}
|
|
31
71
|
>
|
|
32
|
-
{
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
</select>
|
|
38
|
-
</div>
|
|
39
|
-
{window.settings.showPoweredByNyris && <PoweredBy />}
|
|
72
|
+
{window.settings.showPoweredByNyris && <PoweredBy />}
|
|
73
|
+
</div>
|
|
74
|
+
) : (
|
|
75
|
+
window.settings.showPoweredByNyris && <PoweredBy />
|
|
76
|
+
)}
|
|
40
77
|
</div>
|
|
41
78
|
</>
|
|
42
79
|
);
|
|
@@ -12,6 +12,8 @@ import { DEFAULT_REGION } from '../constants';
|
|
|
12
12
|
import { useImageSearch } from 'hooks/useImageSearch';
|
|
13
13
|
import useRequestStore from 'stores/request/requestStore';
|
|
14
14
|
import useResultStore from 'stores/result/resultStore';
|
|
15
|
+
import { hasActiveMachineScope } from 'stores/scope/scopeStore';
|
|
16
|
+
import { firstMachinePartsListingText } from 'stores/machinePartsListing/machinePartsListingStore';
|
|
15
17
|
|
|
16
18
|
function ImagePreviewComponent({
|
|
17
19
|
showAdjustInfo = false,
|
|
@@ -52,7 +54,7 @@ function ImagePreviewComponent({
|
|
|
52
54
|
const detectedRegions = useResultStore(state => state.detectedRegions);
|
|
53
55
|
const resetResultStore = useResultStore(state => state.reset);
|
|
54
56
|
|
|
55
|
-
const { singleImageSearch } = useImageSearch();
|
|
57
|
+
const { singleImageSearch, searchMachinePartsListing } = useImageSearch();
|
|
56
58
|
|
|
57
59
|
const currentIndex = requestImages.length - 1;
|
|
58
60
|
|
|
@@ -86,6 +88,33 @@ function ImagePreviewComponent({
|
|
|
86
88
|
}, []);
|
|
87
89
|
|
|
88
90
|
const onImageRemove = () => {
|
|
91
|
+
if (hasActiveMachineScope()) {
|
|
92
|
+
const keyword = (useRequestStore.getState().query || '').trim();
|
|
93
|
+
resetRegions();
|
|
94
|
+
setRequestImages([]);
|
|
95
|
+
if (keyword) {
|
|
96
|
+
void singleImageSearch({
|
|
97
|
+
text: keyword,
|
|
98
|
+
settings,
|
|
99
|
+
showFeedback: false,
|
|
100
|
+
newSearch: false,
|
|
101
|
+
});
|
|
102
|
+
} else {
|
|
103
|
+
const text = firstMachinePartsListingText();
|
|
104
|
+
useRequestStore.getState().setQuery('');
|
|
105
|
+
useRequestStore.getState().setValueInput('');
|
|
106
|
+
if (text) {
|
|
107
|
+
void searchMachinePartsListing({
|
|
108
|
+
text,
|
|
109
|
+
showFeedback: false,
|
|
110
|
+
clearPostFilters: true,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
navigate('/result');
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
89
118
|
resetRegions();
|
|
90
119
|
setRequestImages([]);
|
|
91
120
|
|
|
@@ -106,6 +135,7 @@ function ImagePreviewComponent({
|
|
|
106
135
|
imageRegion: r,
|
|
107
136
|
showFeedback: true,
|
|
108
137
|
compress: false,
|
|
138
|
+
regionSelectedByUser: true,
|
|
109
139
|
}).then((res: any) => {
|
|
110
140
|
const specificationPrefilter =
|
|
111
141
|
res?.image_analysis?.specification?.prefilter_value || null;
|
|
@@ -12,9 +12,11 @@ import { useUploadSearch } from 'hooks/useUploadSearch';
|
|
|
12
12
|
function ImageUpload({
|
|
13
13
|
onCameraClick,
|
|
14
14
|
disableDisclaimer,
|
|
15
|
+
guideOpen,
|
|
15
16
|
}: {
|
|
16
17
|
onCameraClick?: () => void;
|
|
17
18
|
disableDisclaimer?: boolean;
|
|
19
|
+
guideOpen?: boolean;
|
|
18
20
|
}) {
|
|
19
21
|
const settings = window.settings;
|
|
20
22
|
const user = useAuth0().user;
|
|
@@ -91,7 +93,16 @@ function ImageUpload({
|
|
|
91
93
|
e.target.value = '';
|
|
92
94
|
}}
|
|
93
95
|
/>
|
|
94
|
-
<Tooltip
|
|
96
|
+
<Tooltip
|
|
97
|
+
content={
|
|
98
|
+
guideOpen
|
|
99
|
+
? t('You can now upload an image to search.')
|
|
100
|
+
: t('Search with an image')
|
|
101
|
+
}
|
|
102
|
+
open={guideOpen ? true : undefined}
|
|
103
|
+
side="top"
|
|
104
|
+
sideOffset={10}
|
|
105
|
+
>
|
|
95
106
|
<label
|
|
96
107
|
className={twMerge(
|
|
97
108
|
'min-w-10 min-h-10 flex justify-center items-center cursor-pointer rounded-full bg-gray-100 desktop:bg-transparent hover:bg-gray-100',
|
|
@@ -8,17 +8,20 @@ import { Icon } from '@nyris/nyris-react-components';
|
|
|
8
8
|
import useRequestStore from 'stores/request/requestStore';
|
|
9
9
|
import { getCroppedCanvas } from 'utils/misc';
|
|
10
10
|
import { ToastHelper } from 'helpers/ToastHelper';
|
|
11
|
-
import { Dialog, DialogContent } from 'components/Modal/Dialog';
|
|
11
|
+
import { Dialog, DialogContent, DialogTitle } from 'components/Modal/Dialog';
|
|
12
12
|
import { AutosizeTextarea } from 'components/AutosizeTextArea';
|
|
13
13
|
import Tooltip from 'components/Tooltip/TooltipComponent';
|
|
14
14
|
|
|
15
15
|
import { useTranslation } from 'react-i18next';
|
|
16
16
|
import { createImage } from '../../services/visualSearch';
|
|
17
|
+
import { trackClick } from 'services/Feedback';
|
|
18
|
+
import { CLICK_SUBTYPE } from 'services/feedbackSubtypes';
|
|
17
19
|
interface Props {
|
|
18
20
|
requestImage: any;
|
|
19
21
|
selectedRegion: any;
|
|
20
22
|
setIsInquiryModalOpen: any;
|
|
21
23
|
isInquiryModalOpen?: any;
|
|
24
|
+
allowEmailFallback?: boolean;
|
|
22
25
|
}
|
|
23
26
|
// eslint-disable-next-line
|
|
24
27
|
const emailRegex = /.+\@.+\..+$/;
|
|
@@ -42,6 +45,7 @@ export default function InquiryModal({
|
|
|
42
45
|
selectedRegion,
|
|
43
46
|
setIsInquiryModalOpen,
|
|
44
47
|
isInquiryModalOpen,
|
|
48
|
+
allowEmailFallback = false,
|
|
45
49
|
}: Props) {
|
|
46
50
|
const settings = window.settings;
|
|
47
51
|
const preFilter = useRequestStore(state => state.preFilter);
|
|
@@ -54,6 +58,27 @@ export default function InquiryModal({
|
|
|
54
58
|
const [emailValid, setEmailValid] = useState<boolean | undefined>(undefined);
|
|
55
59
|
|
|
56
60
|
const [information, setInformation] = useState('');
|
|
61
|
+
const useEmailClient =
|
|
62
|
+
allowEmailFallback && !settings.support?.emailTemplateId;
|
|
63
|
+
// encodeURIComponent, not URLSearchParams: mailto (RFC 6068) needs spaces as %20, not +
|
|
64
|
+
const supportEmailHref = `mailto:support@nyris.io?subject=${encodeURIComponent(
|
|
65
|
+
'Vizo support request',
|
|
66
|
+
)}&body=${encodeURIComponent(`Email: ${email.trim()}\n\n${information}`)}`;
|
|
67
|
+
const submitStyle: React.CSSProperties = {
|
|
68
|
+
height: '66px',
|
|
69
|
+
display: 'flex',
|
|
70
|
+
alignItems: 'center',
|
|
71
|
+
width: '50%',
|
|
72
|
+
backgroundColor: emailValid
|
|
73
|
+
? settings.theme?.primaryColor || '#0F766E'
|
|
74
|
+
: '#E9E9EC',
|
|
75
|
+
color: emailValid ? '#fff' : '#AAABB5',
|
|
76
|
+
fontSize: '14px',
|
|
77
|
+
paddingLeft: '16px',
|
|
78
|
+
border: 'none',
|
|
79
|
+
cursor: emailValid ? 'pointer' : 'default',
|
|
80
|
+
textDecoration: 'none',
|
|
81
|
+
};
|
|
57
82
|
|
|
58
83
|
const setFormattedContent = React.useCallback(
|
|
59
84
|
(text: string) => {
|
|
@@ -123,6 +148,7 @@ export default function InquiryModal({
|
|
|
123
148
|
try {
|
|
124
149
|
await emailjs.send(serviceId, templateId, body);
|
|
125
150
|
ToastHelper.success(t('Request sent successfully'));
|
|
151
|
+
trackClick(CLICK_SUBTYPE.RAISE_INQUIRY);
|
|
126
152
|
} catch (error) {
|
|
127
153
|
toast(
|
|
128
154
|
() => {
|
|
@@ -186,7 +212,8 @@ export default function InquiryModal({
|
|
|
186
212
|
>
|
|
187
213
|
<DialogContent
|
|
188
214
|
closeButton={false}
|
|
189
|
-
|
|
215
|
+
aria-describedby={undefined}
|
|
216
|
+
className="w-[360px] desktop:w-[378px] max-w-[calc(100vw-32px)] max-h-[calc(100dvh-32px)] overflow-y-auto flex flex-col bg-white p-0"
|
|
190
217
|
>
|
|
191
218
|
<div>
|
|
192
219
|
<div
|
|
@@ -197,7 +224,8 @@ export default function InquiryModal({
|
|
|
197
224
|
justifyContent: 'space-between',
|
|
198
225
|
}}
|
|
199
226
|
>
|
|
200
|
-
<
|
|
227
|
+
<DialogTitle
|
|
228
|
+
className="text-sm leading-normal tracking-normal"
|
|
201
229
|
style={{
|
|
202
230
|
color: '#2B2C46',
|
|
203
231
|
fontWeight: 'bold',
|
|
@@ -206,8 +234,10 @@ export default function InquiryModal({
|
|
|
206
234
|
{requestImage
|
|
207
235
|
? t('Submit your image for inquiry')
|
|
208
236
|
: t('Submit your inquiry')}
|
|
209
|
-
</
|
|
210
|
-
<
|
|
237
|
+
</DialogTitle>
|
|
238
|
+
<button
|
|
239
|
+
type="button"
|
|
240
|
+
aria-label={t('Close')}
|
|
211
241
|
onClick={() => setIsInquiryModalOpen(false)}
|
|
212
242
|
style={{ display: 'flex', padding: '1px' }}
|
|
213
243
|
>
|
|
@@ -219,7 +249,7 @@ export default function InquiryModal({
|
|
|
219
249
|
cursor: 'pointer',
|
|
220
250
|
}}
|
|
221
251
|
/>
|
|
222
|
-
</
|
|
252
|
+
</button>
|
|
223
253
|
</div>
|
|
224
254
|
{requestImage && (
|
|
225
255
|
<div
|
|
@@ -378,26 +408,31 @@ export default function InquiryModal({
|
|
|
378
408
|
>
|
|
379
409
|
{t('Cancel')}
|
|
380
410
|
</button>
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
411
|
+
{useEmailClient ? (
|
|
412
|
+
<a
|
|
413
|
+
style={submitStyle}
|
|
414
|
+
href={emailValid ? supportEmailHref : undefined}
|
|
415
|
+
aria-disabled={!emailValid}
|
|
416
|
+
tabIndex={emailValid ? 0 : -1}
|
|
417
|
+
onClick={event => {
|
|
418
|
+
if (!emailValid) {
|
|
419
|
+
event.preventDefault();
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
trackClick(CLICK_SUBTYPE.RAISE_INQUIRY);
|
|
423
|
+
}}
|
|
424
|
+
>
|
|
425
|
+
{t('Open email')}
|
|
426
|
+
</a>
|
|
427
|
+
) : (
|
|
428
|
+
<button
|
|
429
|
+
style={submitStyle}
|
|
430
|
+
disabled={!emailValid}
|
|
431
|
+
onClick={handleInquiry}
|
|
432
|
+
>
|
|
433
|
+
{t('Send')}
|
|
434
|
+
</button>
|
|
435
|
+
)}
|
|
401
436
|
</div>
|
|
402
437
|
</div>
|
|
403
438
|
</DialogContent>
|
|
@@ -1,19 +1,46 @@
|
|
|
1
1
|
import { usePagination } from 'react-instantsearch';
|
|
2
2
|
import { Icon } from '@nyris/nyris-react-components';
|
|
3
3
|
import { twMerge } from 'tailwind-merge';
|
|
4
|
+
import { visibleMachinePartsPageIndexes } from 'services/machinePartsPage';
|
|
4
5
|
|
|
5
6
|
export const Pagination = ({
|
|
6
7
|
isLoading,
|
|
7
8
|
className,
|
|
9
|
+
pageCount,
|
|
10
|
+
pageIndex = 0,
|
|
11
|
+
onPageChange,
|
|
8
12
|
}: {
|
|
9
13
|
isLoading: boolean;
|
|
10
14
|
className?: string;
|
|
15
|
+
pageCount?: number;
|
|
16
|
+
pageIndex?: number;
|
|
17
|
+
onPageChange?: (pageIndex: number) => void;
|
|
11
18
|
}) => {
|
|
12
|
-
const
|
|
13
|
-
|
|
19
|
+
const algoliaPagination = usePagination();
|
|
20
|
+
const isControlled = onPageChange !== undefined;
|
|
21
|
+
const currentRefinement = isControlled
|
|
22
|
+
? pageIndex
|
|
23
|
+
: algoliaPagination.currentRefinement;
|
|
24
|
+
const isFirstPage = isControlled
|
|
25
|
+
? currentRefinement <= 0
|
|
26
|
+
: algoliaPagination.isFirstPage;
|
|
27
|
+
const isLastPage = isControlled
|
|
28
|
+
? currentRefinement >= (pageCount ?? 1) - 1
|
|
29
|
+
: algoliaPagination.isLastPage;
|
|
30
|
+
const pages = isControlled
|
|
31
|
+
? visibleMachinePartsPageIndexes(currentRefinement, pageCount ?? 0)
|
|
32
|
+
: algoliaPagination.pages;
|
|
14
33
|
const previousPageIndex = currentRefinement - 1;
|
|
15
34
|
const nextPageIndex = currentRefinement + 1;
|
|
16
35
|
|
|
36
|
+
const selectPage = (page: number) => {
|
|
37
|
+
if (onPageChange) {
|
|
38
|
+
onPageChange(page);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
algoliaPagination.refine(page);
|
|
42
|
+
};
|
|
43
|
+
|
|
17
44
|
return (
|
|
18
45
|
<div
|
|
19
46
|
className={twMerge(
|
|
@@ -22,13 +49,14 @@ export const Pagination = ({
|
|
|
22
49
|
}`,
|
|
23
50
|
className,
|
|
24
51
|
)}
|
|
52
|
+
data-page-count={isControlled ? pageCount : undefined}
|
|
25
53
|
>
|
|
26
54
|
<div className="w-12 h-12 p-3.5 justify-center items-center flex">
|
|
27
55
|
<div
|
|
28
56
|
className="w-5 h-5 relative flex-col justify-start items-start flex cursor-pointer"
|
|
29
57
|
onClick={() => {
|
|
30
58
|
if (isFirstPage) return;
|
|
31
|
-
|
|
59
|
+
selectPage(previousPageIndex);
|
|
32
60
|
}}
|
|
33
61
|
>
|
|
34
62
|
<Icon name="caret_left" className="w-5 h-5" />
|
|
@@ -43,8 +71,7 @@ export const Pagination = ({
|
|
|
43
71
|
className="w-12 px-4 pt-[15px] flex-col justify-end items-center gap-[11px] inline-flex cursor-pointer"
|
|
44
72
|
onClick={event => {
|
|
45
73
|
event.preventDefault();
|
|
46
|
-
|
|
47
|
-
refine(page);
|
|
74
|
+
selectPage(page);
|
|
48
75
|
}}
|
|
49
76
|
>
|
|
50
77
|
<div className="text-center text-sm font-normal leading-[18px] tracking-tight">
|
|
@@ -62,7 +89,7 @@ export const Pagination = ({
|
|
|
62
89
|
className="w-5 h-5 relative flex-col justify-start items-start flex cursor-pointer"
|
|
63
90
|
onClick={() => {
|
|
64
91
|
if (isLastPage) return;
|
|
65
|
-
|
|
92
|
+
selectPage(nextPageIndex);
|
|
66
93
|
}}
|
|
67
94
|
>
|
|
68
95
|
<Icon name="caret_right" className="w-5 h-5" />
|
|
@@ -8,6 +8,8 @@ import { useRefinementList } from 'react-instantsearch';
|
|
|
8
8
|
import { Icon } from '@nyris/nyris-react-components';
|
|
9
9
|
import { useTranslation } from 'react-i18next';
|
|
10
10
|
import useScopeStore from 'stores/scope/scopeStore';
|
|
11
|
+
import { trackClick } from 'services/Feedback';
|
|
12
|
+
import { CLICK_SUBTYPE } from 'services/feedbackSubtypes';
|
|
11
13
|
|
|
12
14
|
function PostFilter({
|
|
13
15
|
attribute,
|
|
@@ -145,7 +147,12 @@ function PostFilter({
|
|
|
145
147
|
<input
|
|
146
148
|
type="checkbox"
|
|
147
149
|
checked={item.isRefined}
|
|
148
|
-
onChange={() =>
|
|
150
|
+
onChange={() => {
|
|
151
|
+
if (!item.isRefined) {
|
|
152
|
+
trackClick(CLICK_SUBTYPE.FILTER_APPLIED);
|
|
153
|
+
}
|
|
154
|
+
refine(item.value);
|
|
155
|
+
}}
|
|
149
156
|
className="cursor-pointer"
|
|
150
157
|
/>
|
|
151
158
|
<span className="text-xs font-normal pl-2 pr-1">
|