iceholidays-frontend 0.4.0 → 0.6.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/actiontext.scss +119 -0
- data/app/assets/stylesheets/iceholidays/frontend/application.sass.scss +968 -355
- data/app/assets/stylesheets/iceholidays/frontend/common.scss +159 -74
- data/app/assets/stylesheets/iceholidays/frontend/layout.scss +218 -125
- data/app/assets/stylesheets/iceholidays/frontend/utils/_antd_overrides.scss +22 -7
- data/app/assets/stylesheets/iceholidays/frontend/utils/_variables.scss +2 -1
- data/app/assets/stylesheets/iceholidays/frontend/widgets/filter_pills.scss +19 -12
- data/app/assets/stylesheets/iceholidays/frontend/widgets/search_bar.scss +4 -0
- data/app/controllers/iceholidays/frontend/posts_controller.rb +14 -0
- data/app/javascript/api-services/agents-api.service.ts +33 -0
- data/app/javascript/api-services/locations-api.service.ts +23 -1
- data/app/javascript/api-services/series-api.service.ts +48 -28
- data/app/javascript/interfaces/agent.interface.ts +11 -0
- data/app/javascript/interfaces/country.interface.ts +4 -3
- data/app/javascript/interfaces/itinerary.interface.ts +101 -8
- data/app/javascript/react/App.tsx +1 -1
- data/app/javascript/react/components/Destinations.tsx +30 -20
- data/app/javascript/react/components/PriceDetails.tsx +146 -0
- data/app/javascript/react/components/shared/ContactAgentsForm.tsx +44 -0
- data/app/javascript/react/components/shared/Headline.tsx +2 -1
- data/app/javascript/react/components/shared/LocationDropdown.tsx +34 -0
- data/app/javascript/react/components/shared/{Postcard.tsx → LocationPostcards.tsx} +22 -1
- data/app/javascript/react/layouts/MainFooter.tsx +64 -39
- data/app/javascript/react/layouts/MainHeader.tsx +68 -30
- data/app/javascript/react/pages/AboutUsPage.tsx +8 -8
- data/app/javascript/react/pages/BlogPage.tsx +6 -4
- data/app/javascript/react/pages/ContactAgentsPage.tsx +174 -5
- data/app/javascript/react/pages/ContactUsPage.tsx +5 -5
- data/app/javascript/react/pages/CountriesPage.tsx +3 -8
- data/app/javascript/react/pages/Homepage.tsx +23 -13
- data/app/javascript/react/pages/ListingPage.tsx +197 -145
- data/app/javascript/react/pages/ShowPage.tsx +275 -265
- data/app/javascript/react/widgets/FilterPills.tsx +83 -49
- data/app/javascript/react/widgets/SearchBarWidget.tsx +24 -8
- data/app/views/iceholidays/frontend/posts/index.html.erb +9 -0
- data/app/views/iceholidays/frontend/posts/show.html.erb +2 -0
- data/config/routes.rb +2 -1
- data/lib/iceholidays/frontend/version.rb +1 -1
- data/public/iceholidays-assets/application.css +1209 -437
- data/public/iceholidays-assets/application.js +91 -104
- data/public/iceholidays-assets/application.js.map +4 -4
- data/public/iceholidays-assets/images/TSTRibbon.png +0 -0
- data/public/iceholidays-assets/images/about-us_logo_mobile.png +0 -0
- data/public/iceholidays-assets/images/destinations_logo.png +0 -0
- data/public/iceholidays-assets/images/footer-bg_mobile.png +0 -0
- data/public/iceholidays-assets/images/logo_mobile.png +0 -0
- metadata +19 -27
- data/public/iceholidays-assets/images/8D7N.png +0 -0
- data/public/iceholidays-assets/images/Frame71.png +0 -0
- data/public/iceholidays-assets/images/africa.png +0 -0
- data/public/iceholidays-assets/images/banner1.png +0 -0
- data/public/iceholidays-assets/images/banner2.png +0 -0
- data/public/iceholidays-assets/images/china.png +0 -0
- data/public/iceholidays-assets/images/china2.png +0 -0
- data/public/iceholidays-assets/images/chongqing.png +0 -0
- data/public/iceholidays-assets/images/guangzhou.png +0 -0
- data/public/iceholidays-assets/images/guilin.png +0 -0
- data/public/iceholidays-assets/images/harbin.png +0 -0
- data/public/iceholidays-assets/images/hongkong.png +0 -0
- data/public/iceholidays-assets/images/inner_mongolia.png +0 -0
- data/public/iceholidays-assets/images/jiangxi.png +0 -0
- data/public/iceholidays-assets/images/kenya.png +0 -0
- data/public/iceholidays-assets/images/kenya2.png +0 -0
- data/public/iceholidays-assets/images/kunming.png +0 -0
- data/public/iceholidays-assets/images/slikroad.png +0 -0
- data/public/iceholidays-assets/images/southafrica.png +0 -0
- data/public/iceholidays-assets/images/tanzania.png +0 -0
- data/public/iceholidays-assets/images/tour1.png +0 -0
- data/public/iceholidays-assets/images/uganda.png +0 -0
- /data/public/iceholidays-assets/images/{Group_71.png → about-us_logo.png} +0 -0
- /data/public/iceholidays-assets/images/{logo_container.png → logo.png} +0 -0
|
@@ -1,120 +1,36 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
|
-
import { Button, Carousel, Col, Flex, Form, Layout, Modal, Row, Select } from "antd";
|
|
2
|
+
import { Button, Carousel, Col, Flex, Form, Layout, Modal, notification, Row, Select, Skeleton, Space } from "antd";
|
|
3
3
|
import SlickButtonFix from "../components/shared/SlickButtonFix";
|
|
4
|
-
import { mdiAccountCash, mdiAccountGroup, mdiAirplane, mdiBagChecked, mdiBedKing, mdiEmailOutline, mdiFileDownload, mdiFlagTriangle, mdiMapMarker, mdiMapMarkerOutline, mdiMenuLeft, mdiMenuRight, mdiPhoneInTalkOutline, mdiReceiptText, mdiShieldAccount, mdiShieldAirplane, mdiSilverwareForkKnife, mdiWhatsapp } from "@mdi/js";
|
|
4
|
+
import { mdiAccountCash, mdiAccountGroup, mdiAirplane, mdiBagChecked, mdiBedKing, mdiClose, mdiEmailOutline, mdiFileDownload, mdiFlagTriangle, mdiMapMarker, mdiMapMarkerOutline, mdiMenuDown, mdiMenuLeft, mdiMenuRight, mdiMenuUp, mdiPhoneInTalkOutline, mdiReceiptText, mdiShieldAccount, mdiShieldAirplane, mdiSilverwareForkKnife, mdiWhatsapp } from "@mdi/js";
|
|
5
5
|
import Icon from "@mdi/react";
|
|
6
6
|
import Headline from "../components/shared/Headline";
|
|
7
7
|
import { Content } from "antd/es/layout/layout";
|
|
8
8
|
import Sider from "antd/es/layout/Sider";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
description: `
|
|
17
|
-
**FREE COVID-19 TRAVEL INSURANCE!!**
|
|
18
|
-
*\*\*Applicable for passenger up to 69 years old (T&C APPLIED)*
|
|
19
|
-
|
|
20
|
-
> All non Malaysia passport holder surcharge RM300 per person!! Except Singapore passport holder.
|
|
21
|
-
|
|
22
|
-
**Special Arrangement:**
|
|
23
|
-
**✓** Essential for traveling abroad!!️ Keeps you in touch with your family at all times - **Travel Sim Card per person per card**.
|
|
24
|
-
**✓** Bottle Mineral Water per person per day.
|
|
25
|
-
**✓**2+1 Luxury VIP Air-conditioned Coach.
|
|
26
|
-
|
|
27
|
-
**Itinerary Highlight:**
|
|
28
|
-
**✓** **Liberation Monument Street** is a great place for locals to relax and hang out on weekends and was also a key location for the movie "I Belonged to You".
|
|
29
|
-
**✓** **Hongyadong** is the most popular attraction in Chongqing and is the number one must-visit attraction in Chongqing because of its peculiar architectural appearance and spectacular night view. Here is not a cave, but a most Bayu traditional characteristics of the foot-hanging building, the whole building is built according to the cliff, has more than 2000 years of history.
|
|
30
|
-
**✓** The remodeled and renovated old town of **Ciqikou** boasts a large number of gourmet and handicraft stores. In addition to the old winding stone road steps, the real life of the old generation of Chongqing people is also shown here.
|
|
31
|
-
**✓** Standing on the **Kaibu Yizhi Park** platform, you will be greeted by the breeze of the river. Drink Ba Ba Tea and taste pastries at the open-air tea houses along the river to experience the leisurely life of Chongqing people.
|
|
32
|
-
**✓** **Xijiang Qianhu Miao Village** it consists of more than ten natural villages built on the hillside, which is the largest Miao settlement in China and even in the world. On the hillside, there are layers and layers of Miao wooden footstools, coffee-colored old houses clinging to the turquoise mountains, which are very characteristic of the nationalities. It is a good place to experience the Miao flavor!
|
|
33
|
-
**✓** As a world-class business card of Guizhou, **Huangguoshu Waterfall** is one of the must-see attractions in Guizhou. The most essential ones are mainly the **Great Falls**, **Tianxingqiao Scenic Area** and **Dou Po Tang waterfall**. The scenic spots of Huangguoshu Falls, such as Shuilian Cave and Dou Po Tang waterfall, were the setting for the 86 version of the TV series \. After viewing the spectacular Huangguoshu Falls in the daytime, when night falls, immerse yourself in a **large-scale light show** staged between the mountains and the water, and feel a different travel experience from daytime play.
|
|
34
|
-
**✓** **Maling River Canyon** is known as "the most beautiful scar on earth". Its **Tian Xing Gallery** is the core part of the essence of the canyon scenic area, which is characterized by the magnificent scale of waterfalls and rock page wall hangings forming the main landscape.
|
|
35
|
-
**✓** **Guiyang netizens hot spots:** Huaxi Shi Zhi Street, Qingyun marketplace, Jai Xiu Pavilion, Hua guo yuan bai gong. **Local Cuisine:**Sichuan Cuisine,Chongqing Jianghu Cuisine, Mala Hot Pot, Long Table Feast of Miao, Sour Soup Fish Flavor, Yao Flavor, Buyi Fortune Feast, Qian Flavor
|
|
36
|
-
|
|
37
|
-
**All 5★ Hotel:*****\*\*Subject to change in the event of conflicting conferences or events.\*\****
|
|
38
|
-
**✓** 2 Nights at Chongqing Hotel
|
|
39
|
-
**✓** 1 Nights at KailiHotel
|
|
40
|
-
**✓** 1 Night at Anshun Hotel
|
|
41
|
-
**✓** 1 Night at XingyiHotel
|
|
42
|
-
**✓** 2 Nights at GuiyangHotel
|
|
43
|
-
**Note: Triple Room Sharing Will Be Base on Extra Roll in Bed Basis!**
|
|
44
|
-
`
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const agentSearchResults = [
|
|
48
|
-
{
|
|
49
|
-
company: "HO WAH GENTING HOLIDAY SDN BHD",
|
|
50
|
-
logo: "/iceholidays-assets/images/hw_logo.png",
|
|
51
|
-
contactNumbers: [ "+6018 982 6422" ],
|
|
52
|
-
address: "Ground Floor No. 31 & 33, Jalan Maharajalela, 50150 Kuala Lumpur",
|
|
53
|
-
phone: "+603 2141 6422",
|
|
54
|
-
whatsapp: "+6018 982 6422",
|
|
55
|
-
email: "enquiries@hwgholidays.com"
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
company: "CHANG HONG TRAVEL & TOUR SDN BHD",
|
|
59
|
-
contactNumbers: [ "+6012 608 0287", "+6018 669 3238" ]
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
]
|
|
9
|
+
import SeriesApi from "../../api-services/series-api.service";
|
|
10
|
+
import { useParams } from "react-router-dom";
|
|
11
|
+
import { Itinerary } from "../../interfaces/itinerary.interface";
|
|
12
|
+
import AgentsApi from "../../api-services/agents-api.service";
|
|
13
|
+
import { Agent } from "../../interfaces/agent.interface";
|
|
14
|
+
import PriceDetails from "../components/PriceDetails";
|
|
15
|
+
import ContactAgentsForm from "../components/shared/ContactAgentsForm";
|
|
63
16
|
|
|
64
17
|
const breadcrumbs = [
|
|
65
18
|
{ title: 'Home' },
|
|
66
19
|
]
|
|
67
20
|
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
{
|
|
82
|
-
date: "23/10/24",
|
|
83
|
-
price: "RM 8,288"
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
date: "23/10/24",
|
|
87
|
-
price: "RM 8,288"
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
date: "23/10/24",
|
|
91
|
-
price: "RM 8,288"
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
date: "23/10/24",
|
|
95
|
-
price: "RM 8,288"
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
date: "23/10/24",
|
|
99
|
-
price: "RM 8,288"
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
date: "23/10/24",
|
|
103
|
-
price: "RM 8,288"
|
|
104
|
-
},
|
|
105
|
-
]
|
|
106
|
-
|
|
107
|
-
const thingsToKnow = [
|
|
108
|
-
{icon: <Icon path={mdiShieldAccount} size={1} />, label: "Free Travel Insurance"},
|
|
109
|
-
{icon: <Icon path={mdiReceiptText} size={1} /> , label: "Airport Taxes"},
|
|
110
|
-
{icon: <Icon path={mdiFlagTriangle} size={1} />, label: "Tour Leader"},
|
|
111
|
-
{icon: <Icon path={mdiBagChecked} size={1} />, label: "Check-in Baggage"},
|
|
112
|
-
{icon: <Icon path={mdiSilverwareForkKnife} size={1} />, label: "Meal Onboard"},
|
|
113
|
-
{icon: <Icon path={mdiBedKing} size={1} />, label: "Hotel"},
|
|
114
|
-
{icon: <Icon path={mdiAccountCash} size={1} />, label: "Gratuities"},
|
|
115
|
-
{icon: <Icon path={mdiAccountGroup} size={1} />, label: "Group Departure"},
|
|
116
|
-
{icon: <Icon path={mdiFileDownload} size={1} />, label: "Itinerary Download EN | CN"}
|
|
117
|
-
]
|
|
21
|
+
const thingsToKnow = (inclusionsData) => {
|
|
22
|
+
return [
|
|
23
|
+
{icon: <Icon path={mdiShieldAccount} size={1} />, label: "Free Travel Insurance", visible: inclusionsData.acf},
|
|
24
|
+
{icon: <Icon path={mdiReceiptText} size={1} /> , label: "Airport Taxes", visible: inclusionsData.airport_taxes},
|
|
25
|
+
{icon: <Icon path={mdiFlagTriangle} size={1} />, label: "Tour Leader", visible: inclusionsData.tour_leader},
|
|
26
|
+
{icon: <Icon path={mdiBagChecked} size={1} />, label: "Check-in Baggage", visible: inclusionsData.luggage},
|
|
27
|
+
{icon: <Icon path={mdiSilverwareForkKnife} size={1} />, label: "Meal Onboard", visible: inclusionsData.meal_onboard},
|
|
28
|
+
{icon: <Icon path={mdiBedKing} size={1} />, label: "Hotel", visible: inclusionsData.hotel},
|
|
29
|
+
{icon: <Icon path={mdiAccountCash} size={1} />, label: "Gratuities", visible: inclusionsData.gratuities},
|
|
30
|
+
{icon: <Icon path={mdiAccountGroup} size={1} />, label: "Group Departure", visible: inclusionsData.group_departure},
|
|
31
|
+
{icon: <Icon path={mdiFileDownload} size={1} />, label: "Itinerary Download EN | CN"}
|
|
32
|
+
];
|
|
33
|
+
}
|
|
118
34
|
|
|
119
35
|
const layoutStyle = {
|
|
120
36
|
overflow: 'hidden',
|
|
@@ -125,20 +41,65 @@ const siderStyle = {
|
|
|
125
41
|
}
|
|
126
42
|
|
|
127
43
|
|
|
128
|
-
|
|
44
|
+
function withParams(Component) {
|
|
45
|
+
return props => <Component {...props} params={useParams()} />;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
class ShowPage extends React.Component <{params;}> {
|
|
49
|
+
seriesApi = new SeriesApi;
|
|
50
|
+
agentsApi = new AgentsApi;
|
|
129
51
|
|
|
130
52
|
state = {
|
|
131
|
-
|
|
53
|
+
loading: true,
|
|
54
|
+
itinerary: {includings:{}, tours:[], priceCurrency:"", country: "", description: "", coverImage: ""},
|
|
55
|
+
selectedTour:{id: null,guaranteed_departure:false, flights:[], caption:"", code:"", prices: []},
|
|
132
56
|
setIsModalOpen: false,
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
57
|
+
loadingAgents: true,
|
|
58
|
+
allAgents: [],
|
|
59
|
+
agents: [],
|
|
60
|
+
states: [],
|
|
61
|
+
cities: [],
|
|
62
|
+
showAgentsResults: false,
|
|
63
|
+
agentData: { name: "", image: "", address: "", phone: "", whatsapp: "", email: "", city: "", state: "" },
|
|
64
|
+
isCollapsed: true,
|
|
65
|
+
showContactAgent: false
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
componentDidMount() {
|
|
70
|
+
this.seriesApi.getItinerary(this.props.params.id)
|
|
71
|
+
.then((itineraryData:Itinerary) => {
|
|
72
|
+
this.setState({itinerary: itineraryData});
|
|
73
|
+
if(itineraryData.tours && itineraryData.tours?.length > 0){
|
|
74
|
+
this.setState({selectedTour: itineraryData.tours[0]});
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
.finally(()=>this.setState({loading: false}))
|
|
78
|
+
.catch(error => {
|
|
79
|
+
notification.error({ message: 'An error occured while loading countries.'});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
this.getAgents();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
getAgents(){
|
|
87
|
+
this.agentsApi.getAgents()
|
|
88
|
+
.then((agentsData:Agent[]) => {
|
|
89
|
+
this.setState({agents: agentsData, allAgents: agentsData});
|
|
90
|
+
this.resetAgents(agentsData);
|
|
91
|
+
})
|
|
92
|
+
.finally(()=>this.setState({loading: false}))
|
|
93
|
+
.catch(error => {
|
|
94
|
+
notification.error({ message: 'An error occured while loading agents.'});
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
selectTour(tourId:number){
|
|
99
|
+
const selectedTour:any = this.state.itinerary.tours.find((tour:any) => tour.id == tourId);
|
|
100
|
+
if(selectedTour){
|
|
101
|
+
this.setState({selectedTour});
|
|
102
|
+
}
|
|
142
103
|
}
|
|
143
104
|
|
|
144
105
|
showModal(agentData){
|
|
@@ -149,27 +110,88 @@ export default class ShowPage extends React.Component {
|
|
|
149
110
|
this.setState({setIsModalOpen: false});
|
|
150
111
|
};
|
|
151
112
|
|
|
113
|
+
|
|
114
|
+
filterCities = (searchStr) => {
|
|
115
|
+
const allAgents:Agent[] = this.state.allAgents;
|
|
116
|
+
|
|
117
|
+
if(searchStr == undefined){
|
|
118
|
+
this.resetAgents();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const agents:Agent[] = allAgents.filter(a => a.state == searchStr);
|
|
122
|
+
const results = agents.map(a => a.city);
|
|
123
|
+
this.setState({cities: results});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
filterStates= (searchStr) => {
|
|
128
|
+
const allAgents:Agent[] = this.state.allAgents;
|
|
129
|
+
|
|
130
|
+
if(searchStr == undefined){
|
|
131
|
+
this.resetAgents();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const agents:Agent[] = allAgents.filter(a => a.city == searchStr);
|
|
136
|
+
const results = agents.map(a => a.state);
|
|
137
|
+
this.setState({states: results});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
resetAgents(agents?){
|
|
141
|
+
const allAgents:Agent[] = agents || this.state.allAgents;
|
|
142
|
+
const states = allAgents.filter(a => a.state?.replace(/\s/g, "") != '').map(a => a.state);
|
|
143
|
+
const cities = allAgents.filter(a => a.city?.replace(/\s/g, "") != '').map(a => a.city);
|
|
144
|
+
let uniqStates = [... new Set(states.map(x=>x))];
|
|
145
|
+
let uniqCities = [... new Set(cities.map(x=>x))];
|
|
146
|
+
this.setState({states: uniqStates, cities: uniqCities});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
findAgents = (formValues) => {
|
|
150
|
+
const allAgents:Agent[] = this.state.allAgents;
|
|
151
|
+
const agents = allAgents.filter(a =>
|
|
152
|
+
(formValues.state == undefined || a.state === formValues.state) &&
|
|
153
|
+
(formValues.city == undefined || a.city === formValues.city));
|
|
154
|
+
this.setState({agents, showAgentsResults: true})
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
toggleDescription = () => {
|
|
159
|
+
const collapsed = this.state.isCollapsed;
|
|
160
|
+
this.setState({isCollapsed: !collapsed});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
toggleContactAgent = () => {
|
|
164
|
+
const collapsed = this.state.showContactAgent;
|
|
165
|
+
this.setState({showContactAgent: !collapsed});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
|
|
152
169
|
render(){
|
|
170
|
+
const {loading, itinerary, selectedTour, agents, states, cities, agentData, isCollapsed, showContactAgent} = this.state;
|
|
171
|
+
const dates = itinerary.tours.length == 0 ? [] : itinerary.tours.map((tour:any) => {
|
|
172
|
+
return {id: tour.id, date: tour.departure_date, price: `${itinerary.priceCurrency} ${tour.price}`}
|
|
173
|
+
});
|
|
174
|
+
|
|
153
175
|
return (
|
|
154
176
|
<div id="show-page">
|
|
155
177
|
|
|
156
178
|
<Layout style={layoutStyle}>
|
|
157
179
|
|
|
158
180
|
<>
|
|
159
|
-
<Headline bannerImage={
|
|
181
|
+
<Headline showSearch={false} bannerImage={itinerary.coverImage} breadcrumbs={breadcrumbs}>
|
|
160
182
|
<div id="show-page_header">
|
|
161
183
|
<Flex justify="space-between" vertical gap={45}>
|
|
162
|
-
<h1>{
|
|
184
|
+
<h1>{selectedTour?.caption}</h1>
|
|
163
185
|
<div className="tour_details">
|
|
164
|
-
<span className="tour_details_country"> <Icon path={mdiMapMarkerOutline} size="16px" /> {
|
|
165
|
-
<span className="tour_details_code"> <Icon path={mdiFileDownload} size="16px" /> {
|
|
186
|
+
<span className="tour_details_country"> <Icon path={mdiMapMarkerOutline} size="16px" /> {itinerary?.country} </span>
|
|
187
|
+
<span className="tour_details_code"> <Icon path={mdiFileDownload} size="16px" /> {selectedTour?.code} </span>
|
|
166
188
|
</div>
|
|
167
189
|
</Flex>
|
|
168
190
|
</div>
|
|
169
191
|
</Headline>
|
|
170
192
|
|
|
171
193
|
<div id="date-selector">
|
|
172
|
-
<Carousel
|
|
194
|
+
<Carousel infinite={false} arrows dots={false} draggable={true} slidesToScroll= {1} slidesToShow={8}
|
|
173
195
|
prevArrow={
|
|
174
196
|
<SlickButtonFix>
|
|
175
197
|
<Icon path={mdiMenuLeft} size={2} />
|
|
@@ -181,13 +203,13 @@ export default class ShowPage extends React.Component {
|
|
|
181
203
|
</SlickButtonFix>
|
|
182
204
|
}>
|
|
183
205
|
{
|
|
184
|
-
dates.map((
|
|
185
|
-
return <div key={index} className=
|
|
206
|
+
dates.map((tour, index) => {
|
|
207
|
+
return <div key={index} className={`date-box ${selectedTour?.id == tour.id && "selected"}`} onClick={()=>this.selectTour(tour.id)}>
|
|
186
208
|
<Flex justify="space-between" align="center" vertical gap={8}>
|
|
187
|
-
<div className="date-box_date"> {
|
|
209
|
+
<div className="date-box_date"> {tour.date} </div>
|
|
188
210
|
<div>
|
|
189
211
|
<span>From</span>
|
|
190
|
-
<div className="date-box_price"> {
|
|
212
|
+
<div className="date-box_price"> {tour.price} </div>
|
|
191
213
|
</div>
|
|
192
214
|
</Flex>
|
|
193
215
|
</div>
|
|
@@ -199,156 +221,142 @@ export default class ShowPage extends React.Component {
|
|
|
199
221
|
|
|
200
222
|
<Layout id="show-page_body">
|
|
201
223
|
<Content>
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
<
|
|
205
|
-
<
|
|
206
|
-
<
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
224
|
+
{
|
|
225
|
+
loading ? (
|
|
226
|
+
<Space direction="vertical" style={{width: '100%'}}>
|
|
227
|
+
<Skeleton/>
|
|
228
|
+
<Skeleton/>
|
|
229
|
+
<Skeleton/>
|
|
230
|
+
</Space>
|
|
231
|
+
) : (
|
|
232
|
+
<div className="details">
|
|
233
|
+
<Flex gap={10} vertical>
|
|
234
|
+
<div className="details-container">
|
|
235
|
+
<div className="details-container_header"> Things to know </div>
|
|
236
|
+
<div className="details-container_content">
|
|
237
|
+
<div id="things-to-know">
|
|
238
|
+
{
|
|
239
|
+
thingsToKnow(itinerary.includings)
|
|
240
|
+
.filter(item => item.visible)
|
|
241
|
+
.map(item => (
|
|
242
|
+
<div className="item">
|
|
243
|
+
<div className="icon"> {item.icon} </div>
|
|
244
|
+
<label>{item.label}</label>
|
|
245
|
+
</div>
|
|
246
|
+
))
|
|
247
|
+
}
|
|
248
|
+
{
|
|
249
|
+
selectedTour?.guaranteed_departure && (
|
|
250
|
+
<div className="item guaranteed">
|
|
251
|
+
<div className="icon"> <Icon path={mdiShieldAirplane} size={1} /></div>
|
|
252
|
+
<label>Guaranteed Departure</label>
|
|
253
|
+
</div>
|
|
254
|
+
)
|
|
255
|
+
}
|
|
256
|
+
</div>
|
|
219
257
|
</div>
|
|
220
258
|
</div>
|
|
221
|
-
</div>
|
|
222
|
-
</div>
|
|
223
259
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
260
|
+
<div className="details-container">
|
|
261
|
+
<div className="details-container_header"> Description </div>
|
|
262
|
+
<div className="details-container_content">
|
|
263
|
+
<div id="description" className={`pre-wrap ${isCollapsed && 'collapsed'}`}>{itinerary?.description}</div>
|
|
264
|
+
<Button className="toggle-description" color="default" variant="filled" block onClick={this.toggleDescription}>{ isCollapsed ? 'Expand' : 'Collapse'}
|
|
265
|
+
<Icon path={isCollapsed ? mdiMenuDown : mdiMenuUp} size="19px" />
|
|
266
|
+
</Button>
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
230
269
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
270
|
+
{
|
|
271
|
+
selectedTour?.flights.length > 0 && (
|
|
272
|
+
<div className="details-container">
|
|
273
|
+
<div className="details-container_header"> Schedule Flight(s) </div>
|
|
274
|
+
<div className="details-container_content">
|
|
275
|
+
<Flex gap={20} vertical>
|
|
276
|
+
{
|
|
277
|
+
selectedTour?.flights.map((flight:any) => (
|
|
278
|
+
<div className="schedule" key={flight.id}>
|
|
279
|
+
<div className="flight-date">
|
|
280
|
+
<Icon path={mdiAirplane} size="12px" />
|
|
281
|
+
<span>{flight.departure_date}</span>
|
|
282
|
+
</div>
|
|
283
|
+
<div className="flight-details">
|
|
284
|
+
<label> {flight.from_airport} > {flight.to_airport} </label>
|
|
285
|
+
<div className="flight-details_airline">
|
|
286
|
+
<img src={flight.airline_logo} />
|
|
287
|
+
<span>{flight.airline}</span>
|
|
288
|
+
</div>
|
|
289
|
+
</div>
|
|
290
|
+
<div className="flight-time">
|
|
291
|
+
<Flex gap={13}>
|
|
292
|
+
<div className="from">
|
|
293
|
+
<label>{flight.departure_time}</label>
|
|
294
|
+
<span>{flight.from_airport}</span>
|
|
295
|
+
</div>
|
|
296
|
+
<div className="flight-time_icon">
|
|
297
|
+
<img src="/iceholidays-assets/images/plane.png" />
|
|
298
|
+
</div>
|
|
299
|
+
<div className="to">
|
|
300
|
+
<label>{flight.arrival_time}</label>
|
|
301
|
+
<span>{flight.to_airport}</span>
|
|
302
|
+
</div>
|
|
303
|
+
</Flex>
|
|
304
|
+
</div>
|
|
305
|
+
</div>
|
|
306
|
+
))
|
|
307
|
+
}
|
|
308
|
+
</Flex>
|
|
245
309
|
</div>
|
|
246
310
|
</div>
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
<div className="from">
|
|
250
|
-
<label>08:55</label>
|
|
251
|
-
<span>Kuala Lumpur (KUL)</span>
|
|
252
|
-
</div>
|
|
253
|
-
<div className="flight-time_icon">
|
|
254
|
-
<img src="/iceholidays-assets/images/plane.png" />
|
|
255
|
-
</div>
|
|
256
|
-
<div className="to">
|
|
257
|
-
<label>12:55</label>
|
|
258
|
-
<span>Guangzhou (CAN)</span>
|
|
259
|
-
</div>
|
|
260
|
-
</Flex>
|
|
261
|
-
</div>
|
|
262
|
-
</div>
|
|
263
|
-
</Flex>
|
|
264
|
-
</div>
|
|
265
|
-
</div>
|
|
311
|
+
)
|
|
312
|
+
}
|
|
266
313
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
<table id="pricing-table">
|
|
271
|
-
<thead>
|
|
272
|
-
<tr>
|
|
273
|
-
<th></th>
|
|
274
|
-
<th className="super-promo">1st-4th Pax
|
|
275
|
-
Super Promo</th>
|
|
276
|
-
<th className="promo">5th-6th Pax
|
|
277
|
-
Promo</th>
|
|
278
|
-
<th className="promo">Promo</th>
|
|
279
|
-
<th className="promo">Promo</th>
|
|
280
|
-
<th className="normal">Normal</th>
|
|
281
|
-
</tr>
|
|
282
|
-
</thead>
|
|
283
|
-
<tbody>
|
|
284
|
-
<tr>
|
|
285
|
-
<td>Adult</td>
|
|
286
|
-
<td className="super-promo">MYR 8,288</td>
|
|
287
|
-
<td className="promo">MYR 8,288</td>
|
|
288
|
-
<td className="promo">MYR 8,288</td>
|
|
289
|
-
<td className="promo">MYR 8,288</td>
|
|
290
|
-
<td className="normal">MYR 8,288</td>
|
|
291
|
-
</tr>
|
|
292
|
-
<tr>
|
|
293
|
-
<td>Child with extra bed</td>
|
|
294
|
-
<td className="super-promo">MYR 8,288</td>
|
|
295
|
-
<td className="promo">MYR 8,288</td>
|
|
296
|
-
<td className="promo">MYR 8,288</td>
|
|
297
|
-
<td className="promo">MYR 8,288</td>
|
|
298
|
-
<td className="normal">MYR 8,288</td>
|
|
299
|
-
</tr>
|
|
300
|
-
</tbody>
|
|
301
|
-
</table>
|
|
302
|
-
</div>
|
|
314
|
+
{selectedTour?.prices.length > 0 && <PriceDetails priceCurrency={itinerary.priceCurrency} prices={selectedTour?.prices}/>}
|
|
315
|
+
|
|
316
|
+
</Flex>
|
|
303
317
|
</div>
|
|
304
|
-
|
|
305
|
-
|
|
318
|
+
)
|
|
319
|
+
}
|
|
306
320
|
</Content>
|
|
307
|
-
<Sider width="265px" style={siderStyle}>
|
|
321
|
+
<Sider width="265px" style={siderStyle} className={`contact-agent-sider ${showContactAgent && 'show-contact-agent-mobile'}`}>
|
|
322
|
+
<div className="toggle_contact-agent" onClick={this.toggleContactAgent}>
|
|
323
|
+
<span>Contact Agent</span>
|
|
324
|
+
</div>
|
|
308
325
|
<div className="contact-agent">
|
|
309
|
-
<div className="contact-agent_header">
|
|
326
|
+
<div className="contact-agent_header">
|
|
327
|
+
<span>Contact Agent</span>
|
|
328
|
+
<Button className="collapse-contact-agent" color="default" variant="text"
|
|
329
|
+
icon={<Icon path={mdiClose} size="19px" />}
|
|
330
|
+
onClick={this.toggleContactAgent}/>
|
|
331
|
+
</div>
|
|
310
332
|
<div className="contact-agent_content">
|
|
311
|
-
<
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
<Form.Item>
|
|
319
|
-
<Button type="primary" block>Find Agent</Button>
|
|
320
|
-
</Form.Item>
|
|
321
|
-
</Form>
|
|
333
|
+
<ContactAgentsForm
|
|
334
|
+
states={states}
|
|
335
|
+
cities={cities}
|
|
336
|
+
findAgents={this.findAgents}
|
|
337
|
+
filterCities={this.filterCities}
|
|
338
|
+
filterStates={this.filterStates}/>
|
|
339
|
+
|
|
322
340
|
{
|
|
323
|
-
this.state.
|
|
341
|
+
this.state.showAgentsResults && (
|
|
324
342
|
<div className="search-results">
|
|
325
343
|
<Flex vertical gap={20}>
|
|
326
344
|
{
|
|
327
|
-
|
|
345
|
+
agents.length > 0 && agents.map((agent:Agent) => {
|
|
328
346
|
return (
|
|
329
347
|
<Row gutter={2} justify="space-between" className="agent-info">
|
|
330
|
-
<Col flex="180px"><span className="company" onClick={()=>this.showModal(
|
|
331
|
-
{
|
|
348
|
+
<Col flex="180px"><span className="company" onClick={()=>this.showModal(agent)}>
|
|
349
|
+
{agent.name}</span>
|
|
332
350
|
</Col>
|
|
333
351
|
<Col flex="none"><Button color="default" variant="filled" shape="circle" size="small" icon={<Icon path={mdiPhoneInTalkOutline} size="16px" />}/></Col>
|
|
334
352
|
<Col flex="none"><Button color="default" variant="filled" shape="circle" size="small" icon={<Icon path={mdiEmailOutline} size="16px" />}/></Col>
|
|
335
353
|
<Col>
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
<
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
<Button size="small" color="primary" variant="outlined">
|
|
343
|
-
<Icon path={mdiWhatsapp} size="12px" />
|
|
344
|
-
{contactNumber}
|
|
345
|
-
</Button>
|
|
346
|
-
)
|
|
347
|
-
})
|
|
348
|
-
}
|
|
349
|
-
</div>
|
|
350
|
-
)
|
|
351
|
-
}
|
|
354
|
+
<div className="contact-numbers">
|
|
355
|
+
<Button size="small" color="primary" variant="outlined">
|
|
356
|
+
<Icon path={mdiWhatsapp} size="12px" />
|
|
357
|
+
{agent.whatsapp}
|
|
358
|
+
</Button>
|
|
359
|
+
</div>
|
|
352
360
|
</Col>
|
|
353
361
|
</Row>
|
|
354
362
|
)
|
|
@@ -365,27 +373,27 @@ export default class ShowPage extends React.Component {
|
|
|
365
373
|
</Layout>
|
|
366
374
|
|
|
367
375
|
|
|
368
|
-
<Modal title={<img src={
|
|
369
|
-
<h2> {
|
|
376
|
+
<Modal title={<img src={agentData?.image}/>} open={this.state.setIsModalOpen} onCancel={()=>this.closeModal()} footer={null} width={325} centered className="agent-full-contact-details">
|
|
377
|
+
<h2> {agentData?.name} </h2>
|
|
370
378
|
<div className="contact-details">
|
|
371
379
|
<Row gutter={[15, 15]} align="middle">
|
|
372
380
|
<Col span={24}>
|
|
373
381
|
<Icon path={mdiMapMarker} size="15px" />
|
|
374
|
-
<span>{
|
|
382
|
+
<span>{agentData?.address}</span>
|
|
375
383
|
</Col>
|
|
376
384
|
{/* </Row>
|
|
377
385
|
<Row gutter={15} align="middle"> */}
|
|
378
|
-
<Col>
|
|
386
|
+
<Col xs={24} sm={24} lg={6}>
|
|
379
387
|
<Icon path={mdiPhoneInTalkOutline} size="15px" />
|
|
380
|
-
<span>{
|
|
388
|
+
<span>{agentData?.phone}</span>
|
|
381
389
|
</Col>
|
|
382
|
-
<Col>
|
|
390
|
+
<Col xs={24} sm={24} lg={6}>
|
|
383
391
|
<Icon path={mdiWhatsapp} size="15px" />
|
|
384
|
-
<span>{
|
|
392
|
+
<span>{agentData?.whatsapp}</span>
|
|
385
393
|
</Col>
|
|
386
|
-
<Col>
|
|
394
|
+
<Col xs={24} sm={24} lg={9}>
|
|
387
395
|
<Icon path={mdiEmailOutline} size="15px" />
|
|
388
|
-
<span>{
|
|
396
|
+
<span>{agentData?.email}</span>
|
|
389
397
|
</Col>
|
|
390
398
|
</Row>
|
|
391
399
|
</div>
|
|
@@ -394,4 +402,6 @@ export default class ShowPage extends React.Component {
|
|
|
394
402
|
)
|
|
395
403
|
}
|
|
396
404
|
|
|
397
|
-
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export default withParams(ShowPage);
|