iceholidays-frontend 0.6.0 → 0.7.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.
- checksums.yaml +4 -4
- data/app/assets/stylesheets/iceholidays/frontend/_slick-theme.scss +194 -0
- data/app/assets/stylesheets/iceholidays/frontend/_slick.scss +100 -0
- data/app/assets/stylesheets/iceholidays/frontend/application.sass.scss +56 -3
- data/app/assets/stylesheets/iceholidays/frontend/common.scss +14 -0
- data/app/assets/stylesheets/iceholidays/frontend/layout.scss +1 -1
- data/app/assets/stylesheets/iceholidays/frontend/utils/_slick_overrides.scss +3 -0
- data/app/javascript/api-services/brochure-api.service.ts +17 -0
- data/app/javascript/api-services/posts-api.service.ts +47 -0
- data/app/javascript/interfaces/blog.interface.ts +8 -0
- data/app/javascript/react/App.tsx +1 -1
- data/app/javascript/react/components/Destinations.tsx +21 -10
- data/app/javascript/react/components/Testimonials.tsx +4 -1
- data/app/javascript/react/components/shared/LocationDropdown.tsx +3 -1
- data/app/javascript/react/layouts/MainHeader.tsx +80 -32
- data/app/javascript/react/pages/BlogPage.tsx +82 -66
- data/app/javascript/react/pages/BlogShowPage.tsx +50 -35
- data/app/javascript/react/pages/ListingPage.tsx +42 -18
- data/app/javascript/react/pages/ShowPage.tsx +84 -52
- data/app/javascript/react/widgets/FilterPills.tsx +21 -18
- data/config/routes.rb +1 -1
- data/lib/iceholidays/frontend/version.rb +1 -1
- data/public/iceholidays-assets/application.css +296 -8
- data/public/iceholidays-assets/application.js +161 -121
- data/public/iceholidays-assets/application.js.map +4 -4
- data/public/iceholidays-assets/images/TST Ribbon@2x.png +0 -0
- data/public/iceholidays-assets/images/TST Ribbon@3x.png +0 -0
- metadata +10 -2
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { mdiFileDownload, mdiMenu } from "@mdi/js";
|
|
2
2
|
import Icon from "@mdi/react";
|
|
3
|
-
import { Button, Drawer, Menu } from "antd";
|
|
3
|
+
import { Button, Drawer, Menu, notification } from "antd";
|
|
4
4
|
import React, { useState } from "react";
|
|
5
5
|
import type { MenuProps } from 'antd';
|
|
6
6
|
import { Link, useLocation, useMatch } from "react-router-dom";
|
|
7
|
+
import BrochueApi from "../../api-services/brochure-api.service";
|
|
7
8
|
|
|
8
9
|
type MenuItem = Required<MenuProps>['items'][number];
|
|
9
10
|
|
|
@@ -40,44 +41,91 @@ const items: MenuItem[] = [
|
|
|
40
41
|
},
|
|
41
42
|
]
|
|
42
43
|
|
|
43
|
-
const MainHeader = () => {
|
|
44
|
-
const [open, setOpen] = useState(false);
|
|
45
|
-
const location = useLocation();
|
|
46
|
-
const activeUrlKey = location.pathname.split("/").pop() || "app";
|
|
47
|
-
const [current, setCurrent] = useState(activeUrlKey);
|
|
48
44
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
45
|
+
function withLocation(Component) {
|
|
46
|
+
return props => <Component {...props} location={useLocation()} />;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
class MainHeader extends React.Component <{location}> {
|
|
50
|
+
brochureApi = new BrochueApi;
|
|
51
|
+
|
|
52
|
+
state = {
|
|
53
|
+
open: false,
|
|
54
|
+
current: "",
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
constructor(props:any){
|
|
58
|
+
super(props);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
componentDidMount() {
|
|
63
|
+
this.setLocation();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
downloadBrochure = () => {
|
|
68
|
+
this.brochureApi.getBrochure()
|
|
69
|
+
.then((brochureUrl:any) => {
|
|
70
|
+
// const blob = brochureUrl.blob();
|
|
71
|
+
// const blobURL = URL.createObjectURL(blob);
|
|
72
|
+
var a = document.createElement("a");
|
|
73
|
+
a.href = brochureUrl;
|
|
74
|
+
a.setAttribute('style', "display: none");
|
|
75
|
+
|
|
76
|
+
a.download = "Brochure";
|
|
77
|
+
document.body.appendChild(a);
|
|
78
|
+
a.click();
|
|
79
|
+
})
|
|
80
|
+
.catch(error => {
|
|
81
|
+
notification.error({ message: 'An error occured while getting brochure.'});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
}
|
|
53
85
|
|
|
54
|
-
const showDrawer = () => {
|
|
55
|
-
setOpen(true);
|
|
56
|
-
};
|
|
57
86
|
|
|
58
|
-
|
|
59
|
-
|
|
87
|
+
private setLocation(){
|
|
88
|
+
const location = this.props.location
|
|
89
|
+
const activeUrlKey = location.pathname.split("/").pop() || "app";
|
|
90
|
+
this.setState({current: activeUrlKey});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
onClick: MenuProps['onClick'] = (e) => {
|
|
95
|
+
this.setState({open: false, current: e.key})
|
|
60
96
|
};
|
|
61
|
-
|
|
62
|
-
return (
|
|
63
|
-
<div id="main-header">
|
|
64
|
-
<Menu mode="horizontal" items={items} onClick={onClick} selectedKeys={[current]} className="nav-menu"/>
|
|
65
97
|
|
|
66
|
-
|
|
98
|
+
showDrawer() {
|
|
99
|
+
this.setState({open: true});
|
|
100
|
+
};
|
|
67
101
|
|
|
68
|
-
|
|
69
|
-
|
|
102
|
+
onClose() {
|
|
103
|
+
this.setState({open: false});
|
|
104
|
+
};
|
|
70
105
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
106
|
+
render(){
|
|
107
|
+
const {open, current } = this.state;
|
|
108
|
+
return (
|
|
109
|
+
<div id="main-header">
|
|
110
|
+
<Menu mode="horizontal" items={items} onClick={this.onClick} selectedKeys={[current]} className="nav-menu"/>
|
|
111
|
+
|
|
112
|
+
<a href="/app"><img src="/iceholidays-assets/images/logo_mobile.png" className="logo logo_mobile"/></a>
|
|
113
|
+
|
|
114
|
+
<div id="nav-buttons">
|
|
115
|
+
<Button onClick={this.downloadBrochure} id="get-brochure" type="primary"><Icon path={mdiFileDownload} size="18px" /> get brochure</Button>
|
|
116
|
+
|
|
117
|
+
<Button id="toggle-menu" type="primary" onClick={()=>this.showDrawer()}>
|
|
118
|
+
<Icon path={mdiMenu} size="25px" />
|
|
119
|
+
</Button>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<Drawer title={null} onClose={()=>this.onClose()} open={open} id="nav-drawer">
|
|
123
|
+
<Menu items={items} onClick={this.onClick} selectedKeys={[current]} className="nav-menu_mobile"/>
|
|
124
|
+
</Drawer>
|
|
74
125
|
</div>
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
</Drawer>
|
|
79
|
-
</div>
|
|
80
|
-
)
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
81
129
|
}
|
|
82
130
|
|
|
83
|
-
export default MainHeader;
|
|
131
|
+
export default withLocation(MainHeader);
|
|
@@ -1,81 +1,97 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import Headline from "../components/shared/Headline";
|
|
3
|
-
import { Button, Card, Col, Row, Space } from "antd";
|
|
3
|
+
import { Button, Card, Col, notification, Row, Space } from "antd";
|
|
4
4
|
import FilterPills from "../widgets/FilterPills";
|
|
5
|
-
import
|
|
5
|
+
import PostsApi from "../../api-services/posts-api.service";
|
|
6
|
+
import { Blog } from "../../interfaces/blog.interface";
|
|
7
|
+
import Markdown from "react-markdown";
|
|
8
|
+
import rehypeRaw from "rehype-raw";
|
|
9
|
+
import remarkGfm from "remark-gfm";
|
|
6
10
|
const { Meta } = Card;
|
|
7
11
|
|
|
8
12
|
const bannerPath = '/iceholidays-assets/images/blog.png';
|
|
9
|
-
const
|
|
10
|
-
{ title:
|
|
11
|
-
{ title:
|
|
13
|
+
const _breadcrumbs = [
|
|
14
|
+
{ title: <a href="/app">Home</a> },
|
|
15
|
+
{ title: <a href="/app/blog">Blog</a> }
|
|
12
16
|
]
|
|
13
17
|
|
|
14
|
-
const types = [ "News", "Blogs" ];
|
|
15
|
-
const blogs = [
|
|
16
|
-
{
|
|
17
|
-
id: 1,
|
|
18
|
-
cover: "/iceholidays-assets/images/blog1.png",
|
|
19
|
-
title: "旅游的意义:探索世界与内心的旅程",
|
|
20
|
-
info: "未分类 / 发表评论 / 2024年11月22日",
|
|
21
|
-
summary: "旅游不仅拓展视野,也带来人生的启示。它教会我们谦逊、适应变化、享受过程,激发内心力量,感恩遇见,并帮助我们找回初心。"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
id: 2,
|
|
25
|
-
cover: "/iceholidays-assets/images/blog1.png",
|
|
26
|
-
title: "旅游的意义:探索世界与内心的旅程",
|
|
27
|
-
info: "未分类 / 发表评论 / 2024年11月22日",
|
|
28
|
-
summary: "旅游不仅拓展视野,也带来人生的启示。它教会我们谦逊、适应变化、享受过程,激发内心力量,感恩遇见,并帮助我们找回初心。"
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
id: 3,
|
|
32
|
-
cover: "/iceholidays-assets/images/blog1.png",
|
|
33
|
-
title: "旅游的意义:探索世界与内心的旅程",
|
|
34
|
-
info: "未分类 / 发表评论 / 2024年11月22日",
|
|
35
|
-
summary: "旅游不仅拓展视野,也带来人生的启示。它教会我们谦逊、适应变化、享受过程,激发内心力量,感恩遇见,并帮助我们找回初心。"
|
|
36
|
-
},
|
|
37
|
-
]
|
|
18
|
+
const types = [ {type: "News", label: "News"},{type: "Blogs", label:"Blogs"} ];
|
|
38
19
|
|
|
39
|
-
const setFilter = (selectedFilter) => {
|
|
40
|
-
console.log("set filter", selectedFilter)
|
|
41
|
-
}
|
|
42
20
|
|
|
21
|
+
export default class BlogPage extends React.Component {
|
|
22
|
+
postsApi = new PostsApi;
|
|
43
23
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
24
|
+
state = {
|
|
25
|
+
blogs: [],
|
|
26
|
+
breadcrumbs: _breadcrumbs,
|
|
27
|
+
searchParamsObj: {type: "", year: "", month: ""},
|
|
28
|
+
}
|
|
47
29
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
30
|
+
componentDidMount() {
|
|
31
|
+
this.postsApi.getBlogs()
|
|
32
|
+
.then(blogsData => {
|
|
33
|
+
this.setState({blogs: blogsData})
|
|
34
|
+
})
|
|
35
|
+
.catch(error => {
|
|
36
|
+
notification.error({ message: 'An error occured while loading blogs.'});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
52
39
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
40
|
+
setFilter = (selectedFilter) => {
|
|
41
|
+
const {breadcrumbs} = this.state;
|
|
42
|
+
breadcrumbs[2] = {title: this.searchParamsToText()};
|
|
43
|
+
|
|
44
|
+
const allHasValues = Object.entries(selectedFilter).every(o => o[1] != "" && o[1] != null);
|
|
45
|
+
if(allHasValues){
|
|
46
|
+
breadcrumbs[1] = {title: <span>Search results</span>};
|
|
47
|
+
}
|
|
48
|
+
this.setState({searchParamsObj: selectedFilter, breadcrumbs})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
searchParamsToText() {
|
|
53
|
+
var {type, year, month} = this.state.searchParamsObj;
|
|
54
|
+
const monthYear = (month || year) && `${month} ${year}`;
|
|
55
|
+
const text = [type, monthYear].filter(val => val).join(", ");
|
|
56
|
+
return <span>{ text } </span>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
render(){
|
|
60
|
+
const {blogs, breadcrumbs, searchParamsObj} = this.state;
|
|
61
|
+
return (
|
|
62
|
+
<div id="blog-page">
|
|
63
|
+
<Headline bannerImage={bannerPath} breadcrumbs={breadcrumbs} title="Blog"/>
|
|
64
|
+
|
|
65
|
+
<div id="blog-page_body">
|
|
66
|
+
<Space size={12} direction="vertical">
|
|
67
|
+
<FilterPills items={types} bindValue="type" allOption={true} initialValue={searchParamsObj} selectFilter={(selected)=>this.setFilter(selected)}></FilterPills>
|
|
68
|
+
</Space>
|
|
69
|
+
|
|
70
|
+
<div id="blogs">
|
|
71
|
+
<Row wrap gutter={20}>
|
|
72
|
+
{
|
|
73
|
+
blogs.map((blog:Blog) => (
|
|
74
|
+
<Col key={blog.id} span={8}>
|
|
75
|
+
<Card cover={<img alt={blog.title} src={blog.thumbnail} />}>
|
|
76
|
+
<Meta title={blog.title} description={
|
|
77
|
+
<>
|
|
78
|
+
{/* <span>{blog.info}</span> */}
|
|
79
|
+
<div className="preview-content">
|
|
80
|
+
<Markdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>{blog.content}</Markdown>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<Button className="view-blog" href={`/app/blog/${blog.id}`} type="link" color="primary" variant="outlined" block>阅读更多</Button>
|
|
84
|
+
</>
|
|
85
|
+
} />
|
|
86
|
+
</Card>
|
|
87
|
+
</Col>
|
|
88
|
+
))
|
|
89
|
+
}
|
|
90
|
+
</Row>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
76
93
|
</div>
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
);
|
|
94
|
+
)
|
|
95
|
+
}
|
|
80
96
|
|
|
81
|
-
|
|
97
|
+
}
|
|
@@ -1,43 +1,58 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import Headline from "../components/shared/Headline";
|
|
3
|
+
import PostsApi from "../../api-services/posts-api.service";
|
|
4
|
+
import { useParams } from "react-router-dom";
|
|
5
|
+
import { notification } from "antd";
|
|
6
|
+
import Markdown from "react-markdown";
|
|
7
|
+
import rehypeRaw from "rehype-raw";
|
|
8
|
+
import remarkGfm from "remark-gfm";
|
|
3
9
|
|
|
4
10
|
const bannerPath = '/iceholidays-assets/images/blog.png';
|
|
5
|
-
const
|
|
11
|
+
const _breadcrumbs = [
|
|
6
12
|
{ title: 'Home' },
|
|
7
13
|
{ title: 'Blog' },
|
|
8
|
-
{ title: '从斯里兰卡到马尔代夫' },
|
|
9
14
|
]
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
16
|
+
function withParams(Component) {
|
|
17
|
+
return props => <Component {...props} params={useParams()} />;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
class BlogShowPage extends React.Component <{params;}> {
|
|
21
|
+
postsApi = new PostsApi;
|
|
22
|
+
|
|
23
|
+
state = {
|
|
24
|
+
breadcrumbs: _breadcrumbs,
|
|
25
|
+
blog: {title: "", thumbnail: "", content: ""},
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
componentDidMount() {
|
|
29
|
+
this.postsApi.getBlog(this.props.params.id)
|
|
30
|
+
.then(blogData => {
|
|
31
|
+
var breadcrumbs = this.state.breadcrumbs;
|
|
32
|
+
breadcrumbs.push({title: blogData.title});
|
|
33
|
+
this.setState({blog: blogData, breadcrumbs})
|
|
34
|
+
})
|
|
35
|
+
.catch(error => {
|
|
36
|
+
notification.error({ message: 'An error occured while loading blog.'});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
render(){
|
|
42
|
+
const {blog} = this.state;
|
|
43
|
+
return (
|
|
44
|
+
<div id="blog-show-page">
|
|
45
|
+
<Headline bannerImage={bannerPath} breadcrumbs={this.state.breadcrumbs} title="Blog"/>
|
|
46
|
+
|
|
47
|
+
<div id="blog-show-page_body">
|
|
48
|
+
<h1>{blog.title}</h1>
|
|
49
|
+
<img src={blog.thumbnail}/>
|
|
50
|
+
<Markdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>{blog.content}</Markdown>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
export default withParams(BlogShowPage);
|
|
@@ -12,6 +12,11 @@ import { Itinerary } from "../../interfaces/itinerary.interface";
|
|
|
12
12
|
|
|
13
13
|
import createDOMPurify from 'dompurify'
|
|
14
14
|
import LocationDropdown from "../components/shared/LocationDropdown";
|
|
15
|
+
import ReactMarkdown from "react-markdown";
|
|
16
|
+
import Markdown from "react-markdown";
|
|
17
|
+
import remarkGfm from "remark-gfm";
|
|
18
|
+
import rehypeRaw from "rehype-raw";
|
|
19
|
+
import rehypeSanitize from "rehype-sanitize";
|
|
15
20
|
|
|
16
21
|
const DOMPurify = createDOMPurify(window)
|
|
17
22
|
|
|
@@ -38,7 +43,7 @@ class ListingPage extends React.Component <{searchParams}> {
|
|
|
38
43
|
|
|
39
44
|
state = {
|
|
40
45
|
countries: [],
|
|
41
|
-
selectedCountry: {cities: []},
|
|
46
|
+
selectedCountry: {name: "", cities: []},
|
|
42
47
|
searchParamsObj: {keyword: "", year: "", month: "", location_id: null},
|
|
43
48
|
itineraries: [],
|
|
44
49
|
location: {name: "", cover: ""},
|
|
@@ -56,15 +61,22 @@ class ListingPage extends React.Component <{searchParams}> {
|
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
componentDidMount() {
|
|
64
|
+
this.setSearchParamsObj();
|
|
65
|
+
|
|
59
66
|
this.locationsApi.getCountries()
|
|
60
67
|
.then(locationsData => {
|
|
61
|
-
|
|
68
|
+
var selectedCountry = locationsData[0];
|
|
69
|
+
const searchParams = this.state.searchParamsObj;
|
|
70
|
+
if(searchParams.location_id){
|
|
71
|
+
const country = locationsData.find(loc => loc.id == searchParams.location_id);
|
|
72
|
+
if(country) selectedCountry = country;
|
|
73
|
+
}
|
|
74
|
+
this.setState({countries: locationsData, selectedCountry})
|
|
75
|
+
this.getItineraries(searchParams, true);
|
|
62
76
|
})
|
|
63
77
|
.catch(error => {
|
|
64
78
|
notification.error({ message: 'An error occured while loading countries.'});
|
|
65
79
|
});
|
|
66
|
-
|
|
67
|
-
this.setSearchParamsObj();
|
|
68
80
|
|
|
69
81
|
}
|
|
70
82
|
|
|
@@ -74,19 +86,18 @@ class ListingPage extends React.Component <{searchParams}> {
|
|
|
74
86
|
searchParams.keys().forEach(key => {
|
|
75
87
|
searchParamsObj[key] = searchParams.get(key);
|
|
76
88
|
});
|
|
77
|
-
|
|
78
|
-
this.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if(locationId) this.getLocation(locationId);
|
|
82
|
-
|
|
83
|
-
this.getItineraries(searchParamsObj);
|
|
89
|
+
|
|
90
|
+
const {breadcrumbs} = this.state;
|
|
91
|
+
breadcrumbs[1] = {title: this.searchParamsToText()};
|
|
92
|
+
this.setState({searchParamsObj, breadcrumbs});
|
|
84
93
|
}
|
|
85
94
|
|
|
86
95
|
|
|
87
96
|
searchParamsToText() {
|
|
88
97
|
var {keyword, year, month} = this.state.searchParamsObj;
|
|
89
|
-
|
|
98
|
+
const monthYear = (month || year) && `${month} ${year}`;
|
|
99
|
+
const text = [(this.state.location?.name || keyword), monthYear].filter(val => val).join(", ");
|
|
100
|
+
return <span>{ text } </span>
|
|
90
101
|
}
|
|
91
102
|
|
|
92
103
|
getLocation(locationId){
|
|
@@ -109,7 +120,17 @@ class ListingPage extends React.Component <{searchParams}> {
|
|
|
109
120
|
});
|
|
110
121
|
}
|
|
111
122
|
|
|
112
|
-
getItineraries(searchParams){
|
|
123
|
+
getItineraries(searchParams, init = false){
|
|
124
|
+
const locationId = searchParams.location_id;
|
|
125
|
+
const storedSearchParamsStr = localStorage.getItem("searchParams");
|
|
126
|
+
if(storedSearchParamsStr != null){
|
|
127
|
+
const storedSearchParams = JSON.parse(storedSearchParamsStr);
|
|
128
|
+
const storedLocationId = storedSearchParams.location_id;
|
|
129
|
+
if(locationId != storedLocationId || (init && locationId)) this.getLocation(locationId);
|
|
130
|
+
}else{
|
|
131
|
+
if(locationId) this.getLocation(locationId);
|
|
132
|
+
}
|
|
133
|
+
|
|
113
134
|
this.seriesApi.getItineraries(searchParams)
|
|
114
135
|
.then(itinerariesData => {
|
|
115
136
|
this.setBreadcrumbs();
|
|
@@ -118,13 +139,16 @@ class ListingPage extends React.Component <{searchParams}> {
|
|
|
118
139
|
.catch(error => {
|
|
119
140
|
notification.error({ message: 'An error occured while loading itineraries.'});
|
|
120
141
|
});
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
localStorage.setItem("searchParams", JSON.stringify(searchParams));
|
|
121
145
|
}
|
|
122
146
|
|
|
123
147
|
setBreadcrumbs(){
|
|
124
148
|
var breadcrumbs = this.state.breadcrumbs;
|
|
125
149
|
if(breadcrumbs.length > 1){
|
|
126
150
|
const {keyword, ...noKeyword} = this.state.searchParamsObj;
|
|
127
|
-
const allHasValues = Object.entries(noKeyword).every(o => o[1] != "");
|
|
151
|
+
const allHasValues = Object.entries(noKeyword).every(o => o[1] != "" && o[1] != null);
|
|
128
152
|
if(allHasValues){
|
|
129
153
|
const searchResultsCrumb = {title: <span>Search results</span>};
|
|
130
154
|
if(breadcrumbs.length > 2){
|
|
@@ -153,7 +177,7 @@ class ListingPage extends React.Component <{searchParams}> {
|
|
|
153
177
|
if(selectedCountry){
|
|
154
178
|
const searchParamsObj = this.state.searchParamsObj;
|
|
155
179
|
searchParamsObj.location_id = selectedCountry.id;
|
|
156
|
-
this.getLocation(selectedCountry.id);
|
|
180
|
+
// this.getLocation(selectedCountry.id);
|
|
157
181
|
|
|
158
182
|
this.setState({selectedCountry, searchParamsObj});
|
|
159
183
|
this.getItineraries(searchParamsObj);
|
|
@@ -170,7 +194,7 @@ class ListingPage extends React.Component <{searchParams}> {
|
|
|
170
194
|
<div id="listing-page_header">
|
|
171
195
|
<Flex vertical gap={10}>
|
|
172
196
|
<h1>{this.searchParamsToText()}</h1>
|
|
173
|
-
<LocationDropdown locations={countries} selectLocation={this.selectCountry}/>
|
|
197
|
+
<LocationDropdown initialValue={selectedCountry.name} locations={countries} selectLocation={this.selectCountry}/>
|
|
174
198
|
</Flex>
|
|
175
199
|
</div>
|
|
176
200
|
</Headline>
|
|
@@ -254,13 +278,13 @@ class ListingPage extends React.Component <{searchParams}> {
|
|
|
254
278
|
</Flex>
|
|
255
279
|
</div>
|
|
256
280
|
</>
|
|
257
|
-
) : <h1 id="no-tours-found">
|
|
281
|
+
) : <h1 id="no-tours-found">No tour package is found.</h1>
|
|
258
282
|
}
|
|
259
283
|
|
|
260
284
|
</div>
|
|
261
285
|
|
|
262
286
|
<Modal title="Description" open={this.state.descriptionModalOpen} onCancel={()=>this.closeModal("descriptionModalOpen")} footer={null} width={1000} centered className="tour_details_description">
|
|
263
|
-
<
|
|
287
|
+
<Markdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>{descriptionData}</Markdown>
|
|
264
288
|
</Modal>
|
|
265
289
|
|
|
266
290
|
<Modal title="Itinerary" open={this.state.itineraryModalOpen} onCancel={()=>this.closeModal("itineraryModalOpen")} footer={null} width={1000} centered className="tour_details_itinerary">
|