@nyris/nyris-webapp 0.3.9 → 0.3.12
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 +11 -11
- package/build/index.html +1 -1
- package/build/{precache-manifest.a97813497ab8d37548141e5e2618d0dc.js → precache-manifest.1b00dd5c15aa0815244681503d6fa9da.js} +9 -9
- package/build/service-worker.js +1 -1
- package/build/static/css/main.0c9239ba.chunk.css +2 -0
- package/build/static/css/main.0c9239ba.chunk.css.map +1 -0
- package/build/static/js/2.520bb6d6.chunk.js +3 -0
- package/build/static/js/{2.6e13adbe.chunk.js.LICENSE.txt → 2.520bb6d6.chunk.js.LICENSE.txt} +0 -0
- package/build/static/js/2.520bb6d6.chunk.js.map +1 -0
- package/build/static/js/main.ef6a9744.chunk.js +2 -0
- package/build/static/js/main.ef6a9744.chunk.js.map +1 -0
- package/package.json +2 -2
- package/src/App.tsx +333 -188
- package/src/actions/nyrisAppActions.ts +69 -65
- package/src/actions/searchActions.ts +301 -195
- package/src/components/CategoryFilter.tsx +16 -13
- package/src/components/Codes.tsx +20 -16
- package/src/components/ExampleImages.tsx +27 -17
- package/src/components/Feedback.tsx +78 -48
- package/src/components/FiltersList.tsx +113 -58
- package/src/components/Header.tsx +29 -17
- package/src/components/PredictedCategories.tsx +15 -12
- package/src/components/Result.tsx +186 -113
- package/src/components/SelectedFiltersSummary.tsx +85 -0
- package/src/components/Sidebar.tsx +44 -34
- package/src/epics/index.ts +173 -104
- package/src/epics/search.ts +214 -139
- package/src/index.css +95 -6
- package/src/index.tsx +147 -145
- package/src/utils.ts +5 -0
- package/build/static/css/main.0481043c.chunk.css +0 -2
- package/build/static/css/main.0481043c.chunk.css.map +0 -1
- package/build/static/js/2.6e13adbe.chunk.js +0 -3
- package/build/static/js/2.6e13adbe.chunk.js.map +0 -1
- package/build/static/js/main.f5da7aa4.chunk.js +0 -2
- package/build/static/js/main.f5da7aa4.chunk.js.map +0 -1
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
import React from
|
|
1
|
+
import React from "react";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
3
|
+
const CategoryFilter = ({ cats }: { cats: string[] }) => {
|
|
4
|
+
if (cats.length === 0) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
return (
|
|
8
|
+
<div id="catlist" style={{ textAlign: "center" }}>
|
|
9
|
+
{
|
|
10
|
+
cats.map((s) => (
|
|
11
|
+
<a key={s} href="#top">
|
|
12
|
+
{s}
|
|
13
|
+
</a>
|
|
14
|
+
)) // TODO fix link
|
|
15
|
+
}
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
15
18
|
};
|
|
16
19
|
|
|
17
20
|
export default CategoryFilter;
|
package/src/components/Codes.tsx
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import {Code} from
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Code } from "@nyris/nyris-api";
|
|
3
3
|
|
|
4
4
|
interface Props {
|
|
5
|
-
|
|
5
|
+
codes: Code[];
|
|
6
6
|
}
|
|
7
|
-
const Codes = ({codes}: Props) =>
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
const Codes = ({ codes }: Props) => (
|
|
8
|
+
<>
|
|
9
|
+
<div className="codes" style={{ textAlign: "center" }}>
|
|
10
|
+
{codes.length > 0 && (
|
|
11
|
+
<span style={{ fontSize: "0.8em" }}>
|
|
12
|
+
Codes
|
|
13
|
+
<br />{" "}
|
|
14
|
+
</span>
|
|
15
|
+
)}
|
|
16
|
+
{codes.map((c, i) => (
|
|
17
|
+
<small key={i} title={c.type}>
|
|
18
|
+
{c.value}
|
|
19
|
+
</small>
|
|
20
|
+
))}
|
|
21
|
+
</div>
|
|
22
|
+
</>
|
|
23
|
+
);
|
|
20
24
|
export default Codes;
|
|
@@ -1,24 +1,34 @@
|
|
|
1
|
-
import React from
|
|
1
|
+
import React from "react";
|
|
2
2
|
|
|
3
3
|
interface ExampleImagesProps {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
images: string[];
|
|
5
|
+
onExampleImageClicked: (url: string) => void;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
const ExampleImages
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
8
|
+
const ExampleImages: React.FC<ExampleImagesProps> = ({
|
|
9
|
+
images,
|
|
10
|
+
onExampleImageClicked,
|
|
11
|
+
}) => {
|
|
12
|
+
if (images.length === 0) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return (
|
|
16
|
+
<section className="useExampleImg">
|
|
17
|
+
You can also try one of these pictures:
|
|
18
|
+
<div className="exampleImages">
|
|
19
|
+
<div className="exImagesWrap">
|
|
20
|
+
{images.map((i) => (
|
|
21
|
+
<img
|
|
22
|
+
key={i}
|
|
23
|
+
src={i}
|
|
24
|
+
alt=""
|
|
25
|
+
onClick={() => onExampleImageClicked(i)}
|
|
26
|
+
/>
|
|
27
|
+
))}
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</section>
|
|
31
|
+
);
|
|
22
32
|
};
|
|
23
33
|
|
|
24
34
|
export default ExampleImages;
|
|
@@ -1,55 +1,85 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import {NyrisFeedbackState} from "../actions/nyrisAppActions";
|
|
3
|
-
import {Animate} from "react-move";
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { NyrisFeedbackState } from "../actions/nyrisAppActions";
|
|
3
|
+
import { Animate } from "react-move";
|
|
4
4
|
|
|
5
5
|
interface FeedbackProps {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
feedbackState: NyrisFeedbackState;
|
|
7
|
+
onPositiveFeedback?: () => void;
|
|
8
|
+
onNegativeFeedback?: () => void;
|
|
9
|
+
onClose?: () => void;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const Feedback: React.FC<FeedbackProps> = ({
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
12
|
+
const Feedback: React.FC<FeedbackProps> = ({
|
|
13
|
+
feedbackState,
|
|
14
|
+
onPositiveFeedback,
|
|
15
|
+
onNegativeFeedback,
|
|
16
|
+
onClose,
|
|
17
|
+
}) => {
|
|
18
|
+
let inner: any = null;
|
|
19
|
+
switch (feedbackState) {
|
|
20
|
+
case "question":
|
|
21
|
+
inner = (
|
|
22
|
+
<div className="feedbackForm">
|
|
23
|
+
<p>Did you find what you were looking for?</p>
|
|
24
|
+
<div
|
|
25
|
+
className="btn primary positiveFeedback"
|
|
26
|
+
onClick={onPositiveFeedback}
|
|
27
|
+
>
|
|
28
|
+
Yes
|
|
29
|
+
</div>
|
|
30
|
+
<div
|
|
31
|
+
className="btn secondary negativeFeedback"
|
|
32
|
+
onClick={onNegativeFeedback}
|
|
33
|
+
>
|
|
34
|
+
No
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
break;
|
|
39
|
+
case "positive":
|
|
40
|
+
inner = (
|
|
41
|
+
<div className="feedbackMessage positive">
|
|
42
|
+
Great, thank you for your feedback!
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
break;
|
|
46
|
+
case "negative":
|
|
47
|
+
inner = (
|
|
48
|
+
<div className="feedbackMessage negative">
|
|
49
|
+
We saved your request so we can track down the issue and improve the
|
|
50
|
+
search experience. Your Feedback helps us to make our service better
|
|
51
|
+
for everyone, thank you!
|
|
52
|
+
<br />
|
|
53
|
+
<div className="btn dismiss" onClick={onClose}>
|
|
54
|
+
Dismiss
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
break;
|
|
59
|
+
default:
|
|
60
|
+
inner = null;
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
return (
|
|
64
|
+
<Animate
|
|
65
|
+
show={feedbackState !== "hidden"}
|
|
66
|
+
start={{ y: 100, opacity: 0 }}
|
|
67
|
+
enter={{ y: [0], opacity: [1] }}
|
|
68
|
+
leave={{ y: [100], opacity: [0] }}
|
|
69
|
+
>
|
|
70
|
+
{({ y, opacity }) => (
|
|
71
|
+
<section
|
|
72
|
+
className="feedback"
|
|
73
|
+
style={{ transform: `translateY(${y}%)`, opacity }}
|
|
74
|
+
>
|
|
75
|
+
<div className="wrapper">{inner}</div>
|
|
76
|
+
<div className="closeFeedbackContainer">
|
|
77
|
+
<div className="closeFeedback" onClick={onClose} />
|
|
78
|
+
</div>
|
|
79
|
+
</section>
|
|
80
|
+
)}
|
|
81
|
+
</Animate>
|
|
82
|
+
);
|
|
53
83
|
};
|
|
54
84
|
|
|
55
85
|
export default Feedback;
|
|
@@ -1,67 +1,122 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { Filter } from "@nyris/nyris-api";
|
|
3
|
+
import {
|
|
4
|
+
addToSelectedFilters,
|
|
5
|
+
removeFromSelectedFilters,
|
|
6
|
+
filterChanged,
|
|
7
|
+
searchFilters,
|
|
8
|
+
} from "../actions/searchActions";
|
|
9
|
+
import { useDispatch } from "react-redux";
|
|
10
|
+
import { RiArrowDropUpLine, RiArrowDropDownLine } from "react-icons/ri";
|
|
11
|
+
import { capitalizeFirstLetter } from "../utils";
|
|
12
|
+
import { AiOutlineSearch } from "react-icons/ai";
|
|
7
13
|
|
|
8
14
|
interface FilterProps {
|
|
9
|
-
|
|
10
|
-
|
|
15
|
+
filter: Filter;
|
|
16
|
+
selectedValues?: string[];
|
|
11
17
|
}
|
|
12
18
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
dispatch(addToSelectedFilters(filter.key , value));
|
|
22
|
-
} else {
|
|
23
|
-
if(filter.key)
|
|
24
|
-
dispatch(removeFromSelectedFilters(filter.key, value));
|
|
25
|
-
}
|
|
19
|
+
const FiltersList: React.FC<FilterProps> = ({ filter, selectedValues }) => {
|
|
20
|
+
const dispatch = useDispatch();
|
|
21
|
+
const [isCollapsed, setIsCollapsed] = useState(false);
|
|
22
|
+
const [message, setMessage] = useState("");
|
|
23
|
+
const handleToggler = () => {
|
|
24
|
+
if (isCollapsed) {
|
|
25
|
+
setIsCollapsed(false);
|
|
26
|
+
return;
|
|
26
27
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
)
|
|
50
|
-
})}
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
|
|
54
|
-
</div>
|
|
28
|
+
setIsCollapsed(true);
|
|
29
|
+
};
|
|
30
|
+
const onFilterChanged = (
|
|
31
|
+
e: React.ChangeEvent<HTMLInputElement>,
|
|
32
|
+
value: string
|
|
33
|
+
) => {
|
|
34
|
+
if (e.target.checked) {
|
|
35
|
+
if (filter.key) dispatch(addToSelectedFilters(filter.key, value));
|
|
36
|
+
} else {
|
|
37
|
+
if (filter.key) dispatch(removeFromSelectedFilters(filter.key, value));
|
|
38
|
+
}
|
|
39
|
+
dispatch(filterChanged());
|
|
40
|
+
};
|
|
41
|
+
// 👇️ called every time input's value changes
|
|
42
|
+
const handleChangeOnSearchInputTyped = (
|
|
43
|
+
e: React.ChangeEvent<HTMLInputElement>
|
|
44
|
+
) => {
|
|
45
|
+
setMessage(e.target.value);
|
|
46
|
+
};
|
|
47
|
+
const queryFilters = (key: string, value: string) => {
|
|
48
|
+
dispatch(searchFilters(key, value));
|
|
49
|
+
};
|
|
55
50
|
|
|
56
|
-
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (message === "") {
|
|
53
|
+
dispatch(searchFilters(filter.key!, ""));
|
|
57
54
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
}, [message, filter.key, dispatch]);
|
|
56
|
+
return (
|
|
57
|
+
<div>
|
|
58
|
+
<div className="item">
|
|
59
|
+
{/* <RiLayoutGridLine className="sidebar-icon" /> */}
|
|
60
|
+
<span className="sidebar-text">
|
|
61
|
+
{" "}
|
|
62
|
+
{capitalizeFirstLetter(filter.key!)}
|
|
63
|
+
</span>
|
|
64
|
+
<RiArrowDropUpLine
|
|
65
|
+
className={
|
|
66
|
+
isCollapsed ? "sidebar-icon" : "sidebar-icon collapsedHide"
|
|
67
|
+
}
|
|
68
|
+
onClick={() => handleToggler()}
|
|
69
|
+
/>
|
|
70
|
+
<RiArrowDropDownLine
|
|
71
|
+
className={
|
|
72
|
+
isCollapsed ? "sidebar-icon collapsedHide" : "sidebar-icon"
|
|
73
|
+
}
|
|
74
|
+
onClick={() => handleToggler()}
|
|
75
|
+
/>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div
|
|
79
|
+
className={
|
|
80
|
+
isCollapsed ? "list-container" : "list-container collapsedHide"
|
|
81
|
+
}
|
|
82
|
+
>
|
|
83
|
+
<div className="searchBar">
|
|
84
|
+
<input
|
|
85
|
+
id="searchQueryInput"
|
|
86
|
+
name="searchQueryInput"
|
|
87
|
+
placeholder="Search"
|
|
88
|
+
value={message}
|
|
89
|
+
onChange={(e) => handleChangeOnSearchInputTyped(e)}
|
|
90
|
+
/>
|
|
91
|
+
<button
|
|
92
|
+
id="searchQuerySubmit"
|
|
93
|
+
type="submit"
|
|
94
|
+
name="searchQuerySubmit"
|
|
95
|
+
onClick={() => queryFilters(filter.key!, message)}
|
|
96
|
+
>
|
|
97
|
+
<AiOutlineSearch />
|
|
98
|
+
</button>
|
|
99
|
+
</div>
|
|
100
|
+
{filter &&
|
|
101
|
+
filter.values &&
|
|
102
|
+
filter.values.length > 0 &&
|
|
103
|
+
filter.values.slice(0, 6).map((item, index) => {
|
|
104
|
+
const checked = selectedValues && selectedValues.includes(item);
|
|
105
|
+
return (
|
|
106
|
+
<div key={index}>
|
|
107
|
+
<input
|
|
108
|
+
value={item}
|
|
109
|
+
checked={checked ? true : false}
|
|
110
|
+
type="checkbox"
|
|
111
|
+
onChange={(e) => onFilterChanged(e, item)}
|
|
112
|
+
/>
|
|
113
|
+
<span className="itemLabel">{item}</span>
|
|
114
|
+
</div>
|
|
115
|
+
);
|
|
116
|
+
})}
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
63
119
|
);
|
|
64
|
-
|
|
65
|
-
}
|
|
120
|
+
};
|
|
66
121
|
|
|
67
|
-
export default FiltersList;
|
|
122
|
+
export default FiltersList;
|
|
@@ -1,20 +1,32 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
|
-
const Header = () =>{
|
|
4
|
-
return (
|
|
3
|
+
const Header = () => {
|
|
4
|
+
return (
|
|
5
5
|
<div id="header">
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
</
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
6
|
+
<section id="branding"></section>
|
|
7
|
+
<div id="menu" className="menuWrap" role="navigation">
|
|
8
|
+
<ul>
|
|
9
|
+
<li>
|
|
10
|
+
<a
|
|
11
|
+
href="https://nyris.io/imprint/#privacy"
|
|
12
|
+
target="_blank"
|
|
13
|
+
rel="noopener noreferrer"
|
|
14
|
+
>
|
|
15
|
+
Privacy Policy
|
|
16
|
+
</a>
|
|
17
|
+
</li>
|
|
18
|
+
<li>
|
|
19
|
+
<a
|
|
20
|
+
href="https://nyris.io/"
|
|
21
|
+
target="_blank"
|
|
22
|
+
rel="noopener noreferrer"
|
|
23
|
+
>
|
|
24
|
+
Visit our Website
|
|
25
|
+
</a>
|
|
26
|
+
</li>
|
|
27
|
+
</ul>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
export default Header;
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
|
|
3
|
-
const PredictedCategories = ({cs}: {cs: {name: string, score: number}[]}) =>
|
|
4
|
-
<>
|
|
5
|
-
{cs.map((c) =>
|
|
6
|
-
<small key={c.name}>
|
|
7
|
-
{c.name === "" ? "No category" : c.name.split(" > ").slice(-1)[0]}:
|
|
8
|
-
{(c.score * 100).toFixed(0)}%
|
|
9
|
-
</small>)}
|
|
10
|
-
</>
|
|
11
|
-
;
|
|
12
|
-
|
|
1
|
+
import React from "react";
|
|
13
2
|
|
|
3
|
+
const PredictedCategories = ({
|
|
4
|
+
cs,
|
|
5
|
+
}: {
|
|
6
|
+
cs: { name: string; score: number }[];
|
|
7
|
+
}) => (
|
|
8
|
+
<>
|
|
9
|
+
{cs.map((c) => (
|
|
10
|
+
<small key={c.name}>
|
|
11
|
+
{c.name === "" ? "No category" : c.name.split(" > ").slice(-1)[0]}:
|
|
12
|
+
{(c.score * 100).toFixed(0)}%
|
|
13
|
+
</small>
|
|
14
|
+
))}
|
|
15
|
+
</>
|
|
16
|
+
);
|
|
14
17
|
export default PredictedCategories;
|